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