Question

I have a set of working imperative code in test and I'm trying to boil it down to an essential test convention.

My test looks like the following:

[Theory, BasicConventions]
public void GetVersionOnSiteVersionControllerReturnsASiteVersion(IFixture fixture)
{
    fixture.OmitAutoProperties = true;
    SiteVersion expected = fixture.Create<SiteVersion>();
    SiteVersion actual = null;

    var sut = fixture.Create<SiteVersionController>();

    var response = sut
        .GetSiteVersion()
        .ExecuteAsync(new CancellationToken())
        .Result
        .TryGetContentValue<SiteVersion>(out actual);

    actual.AsSource().OfLikeness<SiteVersion>().ShouldEqual(expected);
}

I also have a customization that allows this to work, namely by setting the HttpConfiguration and HttpRequestMessage to default non-null values.

public class ApiControllerCustomization : ICustomization
{
    public void Customize(IFixture fixture)
    {
        var origin = fixture.OmitAutoProperties;
        fixture.OmitAutoProperties = true;
        var sut = fixture.Create<SiteVersionController>();
        sut.Configuration = fixture.Create<HttpConfiguration>();
        sut.Request = fixture.Create<HttpRequestMessage>();
        fixture.Inject<SiteVersionController>(sut);
        fixture.OmitAutoProperties = origin;
    }
}

First, this looks ugly, but if I use Build<>().omit().with(config).with(request), it shuts off the automoq customization which it needs to build those instances.

Second, this only works for a SiteVersionController. I'd much rather generalize this for all my ApiControllers (maybe that's a bad idea, but I won't know until I try).

Essentially my convention would be as follows: for all ApiControllers, create them without auto properties but do set the http configuration and request message properties to default non-null values

Was it helpful?

Solution

ApiControllers are quite difficult to wire up, because you'll need to assign certain properties to them in order for everything to work. At the very least, you'll need the Request property to be assigned, because otherwise, the Controller can't invoke Request.CreateResponse. Thus, switching off auto-properties for ApiController instances isn't a good strategy. Instead, you can configure AutoFixture to wire up HttpRequestMessage instances correctly.

Web API 1

With ASP.NET Web API 1, I usually use a Customization like this:

public class WebApiCustomization : ICustomization
{
    public void Customize(IFixture fixture)
    {
        fixture.Customize<HttpRequestMessage>(c => c
            .Do(x =>
                x.Properties.Add(
                    HttpPropertyKeys.HttpConfigurationKey,
                    new HttpConfiguration())));
    }
}

Since auto-properties are on by default, this is going to assign an appropriate instance of HttpRequestMessage to ApiController.Request. Together with an Auto-mocking Customization, Fixture can now create instances of all your ApiController classes.

Web API 2

With ASP.NET Web API 2 (5.0.0.0), things are a little more complicated, but with a bit of trial and error, I got this Customization to pass 808 tests:

public class WebApiCustomization : ICustomization
{
    public void Customize(IFixture fixture)
    {
        fixture.Customize<HttpConfiguration>(c => c
            .OmitAutoProperties());
        fixture.Customize<HttpRequestMessage>(c => c
            .Do(x =>
                x.Properties.Add(
                    HttpPropertyKeys.HttpConfigurationKey,
                    fixture.Create<HttpConfiguration>())));
        fixture.Customize<HttpRequestContext>(c => c
            .Without(x => x.ClientCertificate));
    }
}

Conventions

If you package that into an [AutoData] attribute, you should be able to refactor your test to:

[Theory, BasicConventions]
public void GetVersionOnSiteVersionControllerReturnsASiteVersion(
    SiteVersionController sut,
    SiteVersion expected)
{
    SiteVersion actual = null;

    var response = sut
        .GetSiteVersion()
        .ExecuteAsync(new CancellationToken())
        .Result
        .TryGetContentValue<SiteVersion>(out actual);

    actual.AsSource().OfLikeness<SiteVersion>().ShouldEqual(expected);
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top