30

I am trying to auto mock ApiController class in my test cases. It worked perfectly when I was using WebApi1. I started to use WebApi2 on the new project and I am getting this exception thrown after I try to run my new tests:

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)

My test code:

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

Test case does work if I create sut manually:

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

It's seems that something goes wrong when trying to mock the ControllerContext.RequestContext.ClientCertificate I've tried to create a fixture without it (using AutoFixture .Without() method) but then even the old tests started to fail.

My AutoMoqDataAttribute:

public class AutoMoqDataAttribute : AutoDataAttribute
{
    public AutoMoqDataAttribute()
        : base(new Fixture()
            .Customize(new WebApiCustomization()))
    {
    }
}

WebApi customization:

public class WebApiCustomization : CompositeCustomization
{
    public WebApiCustomization() 
        : base(
        new HttpRequestMessageCustomization(),
        new AutoMoqCustomization())
    {
    }
}

HttpRequestMessage customization:

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'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);
    }
}
user2910739
  • 313
  • 3
  • 7

3 Answers3

35

Note: The original answer requires the same customization to be copied for each new ApiController.

Generalized approach

An alternative way is to automatically fill the Request property on all ApiControllers (thus saving you from cut, copy, and paste):

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

The value of type HttpRequestMessage, for the Request property, is built using the following customization:

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

Packing everything into a composite Customization

Create a Customization composite as below - note that the order of AutoFixture Customizations matter:

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

Hope that helps.

Community
  • 1
  • 1
Nikos Baxevanis
  • 10,868
  • 2
  • 46
  • 80
  • this blog post might also help shed some light on the solution http://blog.ploeh.dk/2013/04/08/how-to-automatically-populate-properties-with-automoq/ – cecilphillip Jun 20 '14 at 21:36
9

Note: Assuming that the UserController class takes an IUserModel through its constructor.

As it looks like, the default constructor of ApiController performs some work (probably more than simple assignments).

If the UserController class takes an IUserModel through its constructor, you can pick that constructor (the greediest) instead.

Update:

Replace the HttpRequestMessageCustomization customization with:

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

And the original test will execute fine.

Nikos Baxevanis
  • 10,868
  • 2
  • 46
  • 80
  • Thanks for quick replay Nikos. I've added your suggested customization, but I still get the same exception thrown when I try to execute the test. – user2910739 Nov 11 '13 at 14:58
  • Did you replace the `HttpRequestMessageCustomization` with the proposed `ApiControllerCustomization`? If yes (and you are still having the same exception) how does the `UsersController` class looks like? – Nikos Baxevanis Nov 11 '13 at 15:08
  • I've tried to add it along side `HttpRequestMessageCustomization` first. Then I've tried to replace it with `ApiControllerCustomization`. I had the same exception in both cases. – user2910739 Nov 11 '13 at 15:44
  • Does the suggestion Customization target classes that *derive* from `ApiController`? – Mark Seemann Nov 11 '13 at 15:44
  • @MarkSeemann it does. I've added `UsersController` code to examples list. – user2910739 Nov 11 '13 at 15:47
  • @MarkSeemann Correct.. I [updated the proposed customization](http://stackoverflow.com/posts/19909129/revisions). – Nikos Baxevanis Nov 11 '13 at 16:43
  • @user2910739 I updated the answer to also customize the `Request` property. Hope that helps... – Nikos Baxevanis Nov 11 '13 at 17:10
  • @NikosBaxevanis it worked! Could you briefly explain how you came up with the solution? – user2910739 Nov 12 '13 at 08:51
  • 1
    @user2910739 Actually the solution came up after a few [small iterations](http://stackoverflow.com/posts/19909129/revisions) :) AFAICT, all we need is to skip all properties except the `Content` which is actually filled using your code from the initial `HttpRequestMessageCustomization` class. – Nikos Baxevanis Nov 12 '13 at 10:07
  • The example in this answer is pretty easy make generic by actually making it a generic class that takes the controller type. I went that route rather than the more verbose solution from the other answer. Thanks! – Kurt Hutchinson Jan 15 '15 at 17:05
  • If you still want AutoMocking, you can add this line within this `Customize` method: `fixture.Customize(new AutoMoqCustomization());` – Aage Jan 10 '17 at 21:37
2

Based on Nikos' answer:

This is a more generic way of using this customization where the controller type can be supplied and the Customization can be used for any controller

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

Then use as follows:

var fixture = new Fixture().Customize(
    new WebApiCustomization<UsersController>());
var sut = fixture.Create<UsersController>();
Aage
  • 5,932
  • 2
  • 32
  • 57