Search code examples
autofixture

Fixture.Customize vs Fixture.Register


Please explain the functional difference between Customize and Register, when to use one over the other. The TestCustomize example below fails and the TestRegister passes. I expected the customize script to work fine. It reads to me in English as: "When generating an HttpClient, use a post-processing lambda on it before providing the specimen".

But what I get is an HTTP address with a GUID in it, clearly generated by AutoFixture.

[Fact]
public void TestCustomize()
{
    var fixture = new Fixture();
    fixture.Customize<HttpClient>(c =>
    {
        //c.OmitAutoProperties(); makes no difference
        c.Do(x => x.BaseAddress = new Uri("http://myval"));
        return c;
    });
    var client = fixture.Create<HttpClient>();
    Assert.Equal("http://myval/", client.BaseAddress.ToString());
}

[Fact]
public void TestRegister()
{
    var fixture = new Fixture();
    fixture.Register(() => new HttpClient
    {
        BaseAddress = new Uri("http://myval")
    });
    var client = fixture.Create<HttpClient>();
    Assert.Equal("http://myval/", client.BaseAddress.ToString());
}

Solution

  • This has nothing to do with Customize vs. Register. In fact, if you look at the source code for Register, you'll see that it's only a convenience method over Customize.

    The problem lies in the use of Do. If you look at the signature of Do, you'll see that it has this type:

    IPostprocessComposer<T> Do(Action<T> action)
    

    Notice that the method returns a value. In Functional style, or, if you will, following Command-Query Separation, the method doesn't mutate the instance on which it's defined, but rather returns a new object with the changed behaviour.

    When one writes:

    c.Do(x => x.BaseAddress = new Uri("http://myval"));
    

    one immediately discards the return value with the changed behaviour. In languages like F# or Haskell, you'd get a compile-time notification if you wrote code like that, telling you that you'd be ignoring the return value of a function call, but C# doesn't do that.

    In any case, AutoFixture's APIs are designed as fluent interfaces. The intent is that you chain method calls together:

    fixture.Customize<HttpClient>(c => c
        .Without(x => x.BaseAddress)
        .Do(x => x.BaseAddress = new Uri("http://myval")));
    

    You still need Without (or, if you will, OmitAutoProperties) to turn off the default auto-property behaviour, because otherwise, BaseAddress will be overwritten by the auto-property feature.

    This version of the test passes:

    [Fact]
    public void TestCustomize()
    {
        var fixture = new Fixture();
        fixture.Customize<HttpClient>(c => c
            .Without(x => x.BaseAddress)
            .Do(x => x.BaseAddress = new Uri("http://myval")));
    
        var client = fixture.Create<HttpClient>();
    
        Assert.Equal("http://myval/", client.BaseAddress.ToString());
    }