Подтвердить что ты не робот

Automocking Web Api 2 контроллер

Я пытаюсь автоматизировать класс 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)

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

[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.Without()), но затем даже старые тесты начали сбой.

My 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 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);
    }
}
4b9b3361

Ответ 1

Примечание: Первоначальный ответ требует, чтобы одна и та же настройка была скопирована для каждого нового 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()));
    }
}

Упаковка всего в составную настройку

Создайте композицию настройки, как показано ниже: обратите внимание, что порядок настроек автоматической настройки:

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

Надеюсь, что это поможет.

Ответ 2

Примечаниедел > Предполагая, что класс 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>()));
    }
}

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

Ответ 3

Основываясь на ответе Никоса:

Это более общий способ использования этой настройки, где может быть указан тип контроллера, и 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>();