2013-11-11 3 views
26

Я пытаюсь автоматизировать класс ApiController в моих тестовых случаях. Он отлично работал, когда я использовал WebApi1. Я начал использовать WebApi2 на новый проект, и я получаю это исключение выброшены после того, как я пытаюсь запустить мои новые испытания:Automocking Web Api 2 controller

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.Security.Cryptography.CryptographicException: pCertContext is an invalid handle. 
    at System.Security.Cryptography.CAPI.CertSetCertificateContextProperty(SafeCertContextHandle pCertContext, UInt32 dwPropId, UInt32 dwFlags, SafeLocalAllocHandle safeLocalAllocHandle) 
    at System.Security.Cryptography.X509Certificates.X509Certificate2.set_Archived(Boolean value) 

Мой тестовый код:

[Theory, AutoMoqData] 
public void approparte_status_code_is_returned(
    string privateKey, 
    UsersController sut) 
{ 
    var response = sut.GetUser(privateKey); 
    var result = response; 

    Assert.Equal(HttpStatusCode.OK, result.StatusCode); 
} 

Тестовый пример работает, если я создаю SUT вручную:

[Theory, AutoMoqData] 
public void approparte_status_code_is_returned(
    string privateKey, 
    [Frozen]Mock<IUserModel> stubModel) 
{ 
    var sut = new UsersController(stubModel.Object); 
    var response = sut.GetUser(privateKey); 
    var result = response; 

    Assert.Equal(HttpStatusCode.OK, result.StatusCode); 
} 

Это кажется, что что-то пойдет не так при попытке дразнить ControllerContext.RequestContext.ClientCertificate я попытался создать прибор без него (с помощью AutoFixture. Без()), но затем даже старые тесты начали сбой.

Мои AutoMoqDataAttribute:

public class AutoMoqDataAttribute : AutoDataAttribute 
{ 
    public AutoMoqDataAttribute() 
     : base(new Fixture() 
      .Customize(new WebApiCustomization())) 
    { 
    } 
} 

WebAPI настройки:

public class WebApiCustomization : CompositeCustomization 
{ 
    public WebApiCustomization() 
     : base(
     new HttpRequestMessageCustomization(), 
     new AutoMoqCustomization()) 
    { 
    } 
} 

HttpRequestMessage настройки:

public class HttpRequestMessageCustomization : ICustomization 
{ 
    public void Customize(IFixture fixture) 
    { 
     fixture.Customize<HttpRequestMessage>(c => c 
      .Without(x => x.Content) 
      .Do(x => 
      { 
       x.Properties[HttpPropertyKeys.HttpConfigurationKey] = new HttpConfiguration(); 
      }) 
      ); 
    } 
} 

UsersController:

/// <summary> 
/// Handles user's account. 
/// </summary> 
[RoutePrefix("api/v1/users/{privateKey:length(64)}")] 
public class UsersController : ApiController 
{ 
    private readonly IUserModel _model; 

    public UsersController(IUserModel model) 
    { 
     _model = model; 
    } 

    /// <summary> 
    /// Returns a user. 
    /// </summary> 
    /// <param name="privateKey">The private key of the user.</param> 
    /// <returns> 
    /// 200 (OK) with user data is returned when user is found. 
    /// 404 (Not found) is returned when user is not found. 
    /// </returns> 
    [HttpGet] 
    [Route("")] 
    public HttpResponseMessage GetUser(string privateKey) 
    { 
     UserProjection projection; 

     try 
     { 
      projection = new UserProjection(_model.Get(privateKey)); 
     } 
     catch (UserNotFoundException) 
     { 
      return new HttpResponseMessage(HttpStatusCode.NotFound); 
     } 

     return Request.CreateResponse(HttpStatusCode.OK, projection); 
    } 
} 
+2

См. Также: http: // stackoverf low.com/a/20723447/126014 –

ответ

33

Примечание: Оригинальный answer требует же настройки для копирования для каждого нового ApiController.

Обобщенного подход

Альтернативный способ для автоматического заполнения Request свойства всех ApiControllers (тем самым избавляя вас от вырезать, копировать и вставить):

internal class ApiControllerCustomization : ICustomization 
{ 
    public void Customize(IFixture fixture) 
    { 
     fixture.Customizations.Add(
      new FilteringSpecimenBuilder(
       new Postprocessor(
        new MethodInvoker(
         new ModestConstructorQuery()), 
        new ApiControllerFiller()), 
       new ApiControllerSpecification())); 
    } 

    private class ApiControllerFiller : ISpecimenCommand 
    { 
     public void Execute(object specimen, ISpecimenContext context) 
     { 
      if (specimen == null) 
       throw new ArgumentNullException("specimen"); 
      if (context == null) 
       throw new ArgumentNullException("context"); 

      var target = specimen as ApiController; 
      if (target == null) 
       throw new ArgumentException(
        "The specimen must be an instance of ApiController.", 
        "specimen"); 

      target.Request = 
       (HttpRequestMessage)context.Resolve(
        typeof(HttpRequestMessage)); 
     } 
    } 

    private class ApiControllerSpecification : IRequestSpecification 
    { 
     public bool IsSatisfiedBy(object request) 
     { 
      var requestType = request as Type; 
      if (requestType == null) 
       return false; 
      return typeof(ApiController).IsAssignableFrom(requestType); 
     } 
    } 
} 

Значения типа HttpRequestMessage , для имущества Request, строится с использованием следующей настройки:

internal class HttpRequestMessageCustomization : ICustomization 
{ 
    public void Customize(IFixture fixture) 
    { 
     fixture.Customize<HttpRequestMessage>(c => c 
      .Without(x => x.Content) 
      .Do(x => x.Properties[HttpPropertyKeys.HttpConfigurationKey] = 
       new HttpConfiguration())); 
    } 
} 

Упаковка все в композитной Кастомизация

Создание Customization композит, как показано ниже - обратите внимание, что the order of AutoFixture Customizations matter:

internal class ApiControllerConventions : CompositeCustomization 
{ 
    internal ApiControllerConventions() 
     : base(
      new HttpRequestMessageCustomization(), 
      new ApiControllerCustomization(), 
      new AutoMoqCustomization()) 
    { 
    } 
} 

Надежда, что помогает.

+5

+1 Это должен быть принятый ответ. –

+2

F # порт: https://gist.github.com/bartelink/9697979 –

+0

это сообщение в блоге также может помочь пролить свет на решение http://blog.ploeh.dk/2013/04/08/how-to- автоматически подставлен-свойства-с-automoq / – cecilphillip

8

Примечание: Предполагая, что UserController класс принимает IUserModel через конструктор.

Как представляется, конструктор по умолчанию ApiController выполняет некоторую работу (возможно, более чем простые назначения).

Если UserController класс принимает IUserModel через конструктор, you can pick that constructor (the greediest) instead.

Update:

Заменить HttpRequestMessageCustomization настройки с:

internal class ApiControllerCustomization : ICustomization 
{ 
    public void Customize(IFixture fixture) 
    { 
     fixture.Customize<HttpRequestMessage>(c => c 
      .Without(x => x.Content) 
      .Do(x => x.Properties[HttpPropertyKeys.HttpConfigurationKey] = 
       new HttpConfiguration())); 

     fixture.Customize<UsersController>(c => c 
      .OmitAutoProperties() 
      .With(x => x.Request, fixture.Create<HttpRequestMessage>())); 
    } 
} 

И оригинальный тест будет выполняться нормально.

+0

Спасибо за быстрый повтор Nikos. Я добавил вашу предложенную настройку, но я все равно получаю то же самое исключение, которое было создано при попытке выполнить тест. – user2910739

+0

Вы заменили «HttpRequestMessageCustomization» на предложенную «ApiControllerCustomization»? Если да (и у вас все еще есть одно и то же исключение), как выглядит класс 'UsersController'? –

+0

Я попытался добавить его сначала в сторону «HttpRequestMessageCustomization». Затем я попытался заменить его «ApiControllerCustomization». В обоих случаях у меня было то же исключение. – user2910739

1

На основании ответа NIKOS':

Это более общий способ использования этой настройки, где тип контроллера может подаваться и Customization может быть использован для любого контроллера

internal class WebApiCustomization<TControllerType> : ICustomization 
    where TControllerType : ApiController 
    { 
     public void Customize(IFixture fixture) 
     { 
      fixture.Customize<HttpRequestMessage>(c => c 
       .Without(x => x.Content) 
       .Do(x => x.Properties[HttpPropertyKeys.HttpConfigurationKey] = 
        new HttpConfiguration())); 

      fixture.Customize<TControllerType>(c => c 
       .OmitAutoProperties() 
       .With(x => x.Request, fixture.Create<HttpRequestMessage>())); 
} 
} 

Затем используйте следующее:

var fixture = new Fixture().Customize(
    new WebApiCustomization<UsersController>()); 
var sut = fixture.Create<UsersController>();