自动锁定Web Api 2控制器

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)

希望有所帮助.


Nik*_*nis 9

注意: 假设UserController该类IUserModel通过其构造函数.

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

如果UserControllerIUserModel通过它的构造函数,你可以选择那个构造函数(最贪婪的).

更新:

用以下内容替换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)

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