2016-12-07 10 views
1

Я настраиваю интеграционное тестирование для WebAPI с использованием в памяти Owin TestServer, и я получаю сообщение об ошибке были найденыOwin.TestServer неправильно находит несколько контроллеров в Web Api 2

несколько типов, которые соответствуют контроллеру с именем 'значения'. Это может произойти, если маршрут, который обслуживает это запрос («апи/{контроллер}») нашел несколько контроллеров, определенных с же именем, но различные пространства имен, которое не поддерживается

в DefaultHttpControllerTypeResolver.GetControllerTypes() ,

ссылка: small project которая воспроизводит ошибку.

Редактировать
В попытке решить эту проблему, я впоследствии попытался использовать обычный HttpServer, HttpSelfHostServer и Owin.SelfHost в дополнение к Owin.TestServer, но все они сталкиваются с той же проблемой.
Редактировать Edit

Узор, при выполнении тестов в пакете, первый тест проходит, но последующие тесты завершаются с ошибкой «Несколько типов были найдены» ошибка. Это происходит в моем фактическом проекте, а также в том, что предоставляется простой репрограммный проект, который дает точно такое же поведение. Однако, как вы можете видеть в репродуцированном проекте, существует только один ValuesController, несмотря на то, что сообщение об ошибке требует иного, и в моем фактическом проекте также не существует нескольких типов контроллеров.

Что я имею в своем основном проекте - это базовый класс интеграционных тестов, который может быть запущен на реальном сервере IIS, а также в памяти с использованием Owin TestServer с использованием производных классов. Когда я запускаю тесты против IIS, они все проходят; когда я запускаю тесты по-разному с помощью Owin TestServer, все они проходят; но когда я запускаю их как пакет в памяти, первый проходит, но остальное всегда терпит неудачу с вышеупомянутой ошибкой.

В попытке получить свежий TestServer перед каждым тестом я создаю новый в TestInitialize(), но это не устраняет проблему.

Учитывая, что все тесты проходят при работе с IIS, я предполагаю, что они должны проходить при запуске с Owin Test Server.

Вот краткое изложение соответствующего кода: WebAPI очень slighly модифицирована из коробки проекта, созданного в Visual Studio 2015.

Здесь класс запуска используется для создания Owin.TestServer

public class Startup 
{   
    public void Configuration(IAppBuilder app) 
    { 
     var config = new HttpConfiguration(); 

     var tracing = config.EnableSystemDiagnosticsTracing(); 
     tracing.IsVerbose = true; 
     tracing.MinimumLevel = System.Web.Http.Tracing.TraceLevel.Debug;   
     // The assembly resolver is shown below. 
     config.Services.Replace(typeof(IAssembliesResolver), new TestApiAssemblyResolver());    

     // Web API routes 
     config.MapHttpAttributeRoutes(); 

     config.Routes.MapHttpRoute(
      name: "DefaultApi", 
      routeTemplate: "api/{controller}", 
      defaults: new { id = RouteParameter.Optional } 
     ); 

     config.Formatters.Remove(config.Formatters.XmlFormatter); 
     app.UseWebApi(config); 

    } 
} 

Тест инициализации выглядит следующим образом ...

[TestInitialize()] 
public void MyTestInitialize() 
{ 
    server = TestServer.Create<Startup>();    
    this.HttpClient = server.HttpClient; 
} 

Вот сборка распознаватель используется для загрузки сборки WebAPI в тест. Без этого тесты терпят неудачу с ошибкой Http NotFound.

public class TestApiAssemblyResolver : DefaultAssembliesResolver 
{ 
    public override ICollection<Assembly> GetAssemblies() 
    { 
     List<Assembly> baseAssemblies = AppDomain.CurrentDomain.GetAssemblies().ToList(); 
     var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "webapiCheck.dll"); 
     /* 
     You can try unreferencing webapiCheck.dll from this project but it makes no difference. 
     */ 
     //var path = @"Your path...Documents\visual studio 2015\Projects\WebApiCheck\WebApiCheck\bin\webapiCheck.dll"; 

     var controllersAssembly = Assembly.LoadFrom(path); 
     baseAssemblies.Add(controllersAssembly); 
     return baseAssemblies; 
    } 

} 

Вот тестовый метод ...

[TestMethod] 
public async Task GetValues() 
{ 
    uri = "api/values"; 
    var response = await GetAsync();    

    if (response.IsSuccessStatusCode) 
    { 
     var result = await response.Content.ReadAsAsync<IEnumerable<string>>(); 
     Assert.AreEqual(2, result.Count()); 
    } 
    else 
    { 
     Assert.Fail(response.ReasonPhrase); 
    } 

} 

Я попытался заменить DefaultHttpControllerSelector работать вокруг проблемы, но встречаются ошибки там, потому что base.GetControllerMapping() иногда не возвращает результат.

public class BypassCacheSelector : DefaultHttpControllerSelector 
    { 
     private readonly HttpConfiguration _configuration; 

     public BypassCacheSelector(HttpConfiguration configuration) 
      : base(configuration) 
     { 
      _configuration = configuration; 
     } 

     public override HttpControllerDescriptor SelectController(HttpRequestMessage request) 
     { 
      var path = ConfigurationManager.AppSettings["ApiAssemblyLocation"]; 

      var assembly = Assembly.LoadFrom(path); 
      var types = assembly.GetTypes(); //GetExportedTypes doesn't work with dynamic assemblies 
      var matchedTypes = types.Where(i => typeof(IHttpController).IsAssignableFrom(i)).ToList(); 

      // base.GetControllerMapping() sometimes returns no result; 
      var map = base.GetControllerMapping();    

      string absPath = request.RequestUri.AbsolutePath; 
      var candidateName = absPath.Substring(absPath.LastIndexOf("/")+1); 
      string controllerName = null; 
      foreach (var m in map) 
      { 
       if (candidateName.StartsWith(m.Key)) 
       { 
        controllerName = m.Key; 
        break; 
       }     
      } 

      var matchedController = 
       matchedTypes.FirstOrDefault(i => i.Name.ToLower() == controllerName.ToLower() + "controller"); 

      return new HttpControllerDescriptor(_configuration, controllerName, matchedController); 
     } 
    } 

Чтобы убедиться, что проблема не связана с атрибутом маршрутизации я удалил атрибут маршрутизации от REPRO проекта и действительно проблема сохраняется: при запуске поодиночке тесты проходят, но при запуске в партии они не с той же ошибкой ,

Если кто-то может указать, как правильно использовать Owin.TestServer с WebApi, я был бы признателен.

ответ

0

Проблема была в TestApiAssemblyResolver. Во втором и последующих тестах сборщики контроллеров загружались более одного раза в baseAssemblies, поэтому контроллер Selector не мог решить, какой из них выбрать. Решение состояло в том, чтобы проверить baseAssemblies и добавить только сборку контроллеров, если она еще не была в baseAssemblies.

Вот обновленный код, который теперь отлично работает.

public class TestApiAssemblyResolver : DefaultAssembliesResolver 
{ 
    public override ICollection<Assembly> GetAssemblies() 
    { 
     List<Assembly> baseAssemblies = AppDomain.CurrentDomain.GetAssemblies().ToList(); 

     var path = ConfigurationManager.AppSettings["ApiAssemblyLocation"]; 

     var controllersAssembly = Assembly.LoadFrom(path); 
     // this next line fixed the problem: the second and subsequent time 
     // the controller assembly was in baseAssemblies multiple times 
     // which caused the problem. 
     if (!baseAssemblies.Contains(controllersAssembly)) 
     { 
      baseAssemblies.Add(controllersAssembly); 
     }    

     return baseAssemblies; 
    } 

}