It may be tempting to ask the question of how do I make AutoFixture adapt to my design?, but often, a more interesting question could be: how do I make my design more robust?
You can keep the design and 'fix' AutoFixture, but I don't think it's a particularly good idea.
Before I tell you how to do that, depending on your requirements, perhaps all you need to do is the following.
Explicit assignment
Why not simply assign a valid value to ExpirationDate
, like this?
var sc = fixture.Create<SomeClass>();
sc.ExpirationDate = sc.ValidFrom + fixture.Create<TimeSpan>();
// Perform test here...
If you're using AutoFixture.Xunit, it can be even simpler:
[Theory, AutoData]
public void ExplicitPostCreationFix_xunit(
SomeClass sc,
TimeSpan duration)
{
sc.ExpirationDate = sc.ValidFrom + duration;
// Perform test here...
}
This is fairly robust, because even though AutoFixture (IIRC) creates random TimeSpan
values, they'll stay in the positive range unless you've done something to your fixture
to change its behaviour.
This approach would be the simplest way to address your question if you need to test SomeClass
itself. On the other hand, it's not very practical if you need SomeClass
as input values in myriads of other tests.
In such cases, it can be tempting to fix AutoFixture, which is also possible:
Changing AutoFixture's behaviour
Now that you've seen how to address the problem as a one-off solution, you can tell AutoFixture about it as a general change of the way SomeClass
is generated:
fixture.Customize<SomeClass>(c => c
.Without(x => x.ValidFrom)
.Without(x => x.ExpirationDate)
.Do(x =>
{
x.ValidFrom = fixture.Create<DateTime>();
x.ExpirationDate =
x.ValidFrom + fixture.Create<TimeSpan>();
}));
// All sorts of other things can happen in between, and the
// statements above and below can happen in separate classes, as
// long as the fixture instance is the same...
var sc = fixture.Create<SomeClass>();
You can also package the above call to Customize
in an ICustomization
implementation, for further reuse. This would also enable you to use a customized Fixture
instance with AutoFixture.Xunit.
Change the design of the SUT
While the above solutions describe how to change the behaviour of AutoFixture, AutoFixture was originally written as a TDD tool, and the main point of TDD is to provided feedback about the System Under Test (SUT). AutoFixture tends to amplify that sort of feedback, which is also the case here.
Consider the design of SomeClass
. Nothing prevents a client from doing something like this:
var sc = new SomeClass
{
ValidFrom = new DateTime(2015, 2, 20),
ExpirationDate = new DateTime(1900, 1, 1)
};
This compiles and runs without errors, but is probably not what you want. Thus, AutoFixture is actually not doing anything wrong; SomeClass
isn't properly protecting its invariants.
This is a common design mistake, where developers tend to put too much trust into the semantic information of the members' names. The thinking seems to be that no-one in their right mind would set ExpirationDate
to a value before ValidFrom
! The problem with that sort of argument is that it assumes that all developers will always be assigning these values in pairs.
However, clients may also get a SomeClass
instance passed to them, and want to update one of the values, e.g.:
sc.ExpirationDate = new DateTime(2015, 1, 31);
Is this valid? How can you tell?
The client could look at sc.ValidFrom
, but why should it? The whole purpose of encapsulation is to relieve clients of such burdens.
Instead, you should consider changing the design SomeClass
. The smallest design change I can think of is something like this:
public class SomeClass
{
public DateTime ValidFrom { get; set; }
public TimeSpan Duration { get; set; }
public DateTime ExpirationDate
{
get { return this.ValidFrom + this.Duration; }
}
}
This turns ExpirationDate
into a read-only, calculated property. With this change, AutoFixture just works out of the box:
var sc = fixture.Create<SomeClass>();
// Perform test here...
You can also use it with AutoFixture.Xunit:
[Theory, AutoData]
public void ItJustWorksWithAutoFixture_xunit(SomeClass sc)
{
// Perform test here...
}
This is still a little brittle, because although by default, AutoFixture creates positive TimeSpan
values, it's possible to change that behaviour as well.
Furthermore, the design actually allows clients to assign negative TimeSpan
values to the Duration
property:
sc.Duration = TimeSpan.FromHours(-1);
Whether or not this should be allowed is up to the Domain Model. Once you begin to consider this possibility, it may actually turn out that defining time periods that move backwards in time is valid in the domain...
Design according to Postel's Law
If the problem domain is one where going back in time isn't allowed, you could consider adding a Guard Clause to the Duration
property, rejecting negative time spans.
However, personally, I often find that I arrive at a better API design when I take Postel's Law seriously. In this case, why not change the design so that SomeClass
always uses the absolute TimeSpan instead of the signed TimeSpan
?
In that case, I'd prefer an immutable object that doesn't enforce the roles of two DateTime
instances until it knows their values:
public class SomeClass
{
private readonly DateTime validFrom;
private readonly DateTime expirationDate;
public SomeClass(DateTime x, DateTime y)
{
if (x < y)
{
this.validFrom = x;
this.expirationDate = y;
}
else
{
this.validFrom = y;
this.expirationDate = x;
}
}
public DateTime ValidFrom
{
get { return this.validFrom; }
}
public DateTime ExpirationDate
{
get { return this.expirationDate; }
}
}
Like the previous redesign, this just works out of the box with AutoFixture:
var sc = fixture.Create<SomeClass>();
// Perform test here...
The situation is the same with AutoFixture.Xunit, but now no clients can misconfigure it.
Whether or not you find such a design appropriate is up to you, but I hope at least it's food for thought.