自动锁定Web Api 2控制器

我试图在我的测试用例中自动模拟ApiController类。 当我使用WebApi1时,它工作得很好。 我开始在新项目中使用WebApi2,并且在尝试运行新测试后抛出此exception:

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 stubModel) { var sut = new UsersController(stubModel.Object); var response = sut.GetUser(privateKey); var result = response; Assert.Equal(HttpStatusCode.OK, result.StatusCode); } 

在尝试模拟ControllerContext.RequestContext.ClientCertificate时,似乎出现了问题。我试图在没有它的情况下创建一个fixture(使用AutoFixture .Without()方法),但是即使旧的测试也开始失败。

我的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(c => c .Without(x => x.Content) .Do(x => { x.Properties[HttpPropertyKeys.HttpConfigurationKey] = new HttpConfiguration(); }) ); } } 

UsersController:

 ///  /// Handles user's account. ///  [RoutePrefix("api/v1/users/{privateKey:length(64)}")] public class UsersController : ApiController { private readonly IUserModel _model; public UsersController(IUserModel model) { _model = model; } ///  /// Returns a user. ///  /// The private key of the user. ///  /// 200 (OK) with user data is returned when user is found. /// 404 (Not found) is returned when user is not found. ///  [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); } } 

注意 :原始答案要求为每个新的ApiController复制相同的自定义。

广义方法

另一种方法是自动填充所有ApiControllers上的Request属性(从而避免剪切,复制和粘贴):

 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); } } } 

对于Request属性,类型HttpRequestMessage的值是使用以下自定义构建的:

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

将所有内容打包成复合定制

创建一个自定义组合如下 – 请注意AutoFixture自定义的顺序很重要 :

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

希望有所帮助。

注意 假设UserController类通过其构造函数获取IUserModel

看起来, ApiController的默认构造ApiController执行一些工作(可能不仅仅是简单的赋值)。

如果UserController类通过其构造函数获取IUserModel ,则可以选择构造函数( 最贪婪 ) 。

更新

用以下内容替换HttpRequestMessageCustomization自定义:

 internal class ApiControllerCustomization : ICustomization { public void Customize(IFixture fixture) { fixture.Customize(c => c .Without(x => x.Content) .Do(x => x.Properties[HttpPropertyKeys.HttpConfigurationKey] = new HttpConfiguration())); fixture.Customize(c => c .OmitAutoProperties() .With(x => x.Request, fixture.Create())); } } 

并且原始测试将执行正常。

基于Nikos的回答:

这是使用此自定义的更通用方式,其中可以提供控制器类型,并且可以将Customization用于任何控制器

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

然后使用如下:

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