自动锁定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 ();