use*_*739 30 c# autofixture automoq
我试图在我的测试用例中自动模拟ApiController类.当我使用WebApi1时,它工作得很好.我开始在新项目中使用WebApi2,并且在尝试运行新测试后抛出此异常:
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)
Run Code Online (Sandbox Code Playgroud)
我的测试代码:
[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);
}
Run Code Online (Sandbox Code Playgroud)
如果我手动创建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);
}
Run Code Online (Sandbox Code Playgroud)
在尝试模拟ControllerContext.RequestContext.ClientCertificate时,似乎出现了问题.我试图在没有它的情况下创建一个fixture(使用AutoFixture .Without()方法),但是即使是旧的测试也开始失败.
我的AutoMoqDataAttribute:
public class AutoMoqDataAttribute : AutoDataAttribute
{
public AutoMoqDataAttribute()
: base(new Fixture()
.Customize(new WebApiCustomization()))
{
}
}
Run Code Online (Sandbox Code Playgroud)
WebApi定制:
public class WebApiCustomization : CompositeCustomization
{
public WebApiCustomization()
: base(
new HttpRequestMessageCustomization(),
new AutoMoqCustomization())
{
}
}
Run Code Online (Sandbox Code Playgroud)
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();
})
);
}
}
Run Code Online (Sandbox Code Playgroud)
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);
}
}
Run Code Online (Sandbox Code Playgroud)
Nik*_*nis 35
注意:原始答案要求为每个新的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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
类型的值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()));
}
}
Run Code Online (Sandbox Code Playgroud)
将所有内容打包成复合定制
创建自定义组合如下 - 请注意AutoFixture自定义的顺序很重要:
internal class ApiControllerConventions : CompositeCustomization
{
internal ApiControllerConventions()
: base(
new HttpRequestMessageCustomization(),
new ApiControllerCustomization(),
new AutoMoqCustomization())
{
}
}
Run Code Online (Sandbox Code Playgroud)
希望有所帮助.
注意:
假设UserController该类IUserModel通过其构造函数.
看起来,默认构造函数ApiController执行一些工作(可能不仅仅是简单的赋值).
如果UserController类IUserModel通过它的构造函数,你可以选择那个构造函数(最贪婪的).
更新:
用以下内容替换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>()));
}
}
Run Code Online (Sandbox Code Playgroud)
并且原始测试将执行正常.