Here's my object:
public class Symbol
{
private readonly string _identifier;
private readonly IList<Quote> _historicalQuotes;
public Symbol(string identifier, IEnumerable<Quote> historicalQuotes = null)
{
_identifier = identifier;
_historicalQuotes = historicalQuotes;
}
}
public class Quote
{
private readonly DateTime _tradingDate;
private readonly decimal _open;
private readonly decimal _high;
private readonly decimal _low;
private readonly decimal _close;
private readonly decimal _closeAdjusted;
private readonly long _volume;
public Quote(
DateTime tradingDate,
decimal open,
decimal high,
decimal low,
decimal close,
decimal closeAdjusted,
long volume)
{
_tradingDate = tradingDate;
_open = open;
_high = high;
_low = low;
_close = close;
_closeAdjusted = closeAdjusted;
_volume = volume;
}
}
I need an instance of Symbol populated with a list of Quote.
An in my test, I want to verify that I can return all quotes which the close price is under or above a specific value. Here's my test:
[Fact]
public void PriceUnder50()
{
var msftIdentifier = "MSFT";
var quotes = new List<Quote>
{
new Quote(DateTime.Parse("01-01-2009"), 0, 0, 0, 49, 0, 0),
new Quote(DateTime.Parse("01-02-2009"), 0, 0, 0, 51, 0, 0),
new Quote(DateTime.Parse("01-03-2009"), 0, 0, 0, 50, 0, 0),
new Quote(DateTime.Parse("01-04-2009"), 0, 0, 0, 10, 0, 0)
};
_symbol = new Symbol(msftIdentifier, quotes);
var indicator = new UnderPriceIndicator(50);
var actual = indicator.Apply(_symbol);
Assert.Equal(2, actual.Count);
Assert.True(actual.Any(a => a.Date == DateTime.Parse("01-01-2009")));
Assert.True(actual.Any(a => a.Date == DateTime.Parse("01-04-2009")));
Assert.True(actual.Any(a => a.Price == 49));
Assert.True(actual.Any(a => a.Price == 10));
}
OK.
Now, I wanna do it using Autofixture, im a real beginner. I've read pretty much everything I could on internet about this tool (the author's blog, codeplex FAQ, github source code). I understand the basic features of autofixture, but now I wanna use autofixture in my real project. Here's what I tried so far.
var msftIdentifier = "MSFT";
var quotes = new List<Quote>();
var random = new Random();
fixture.AddManyTo(
quotes,
() => fixture.Build<Quote>().With(a => a.Close, random.Next(1,49)).Create());
quotes.Add(fixture.Build<Quote>().With(a => a.Close, 49).Create());
_symbol = new Symbol(msftIdentifier, quotes);
// I would just assert than 49 is in the list
Assert.True(_symbol.HistoricalQuotes.Contains(new Quote... blabla 49));
Ideally, I would prefer to directly create a fixture of Symbol, but I don't know how to customize my list of quotes. And I'm not sure that my test is generic, because, in another test, I will need to check that a specific value is above instead of under, so I'm gonna duplicate the "fixture code" and manual add a quote = 51.
So my questions are:
1 - Is it the way how I'm supposed to use autofixture?
2 - Is it possible to improve the way I use autofixture in my example ?
AutoFixture was originally build as a tool for Test-Driven Development (TDD), and TDD is all about feedback. In the spirit of GOOS, you should listen to your tests. If the tests are hard to write, you should consider your API design. AutoFixture tends to amplify that sort of feedback, and here's what it's telling me.
Make comparison easier
First, while not related to AutoFixture, the Quote
class just begs to be turned into a proper Value Object, so I'll override Equals
to make it easier to compare expected and actual instances:
public override bool Equals(object obj)
{
var other = obj as Quote;
if (other == null)
return base.Equals(obj);
return _tradingDate == other._tradingDate
&& _open == other._open
&& _high == other._high
&& _low == other._low
&& _close == other._close
&& _closeAdjusted == other._closeAdjusted
&& _volume == other._volume;
}
(Make sure to override GetHashCode
too.)
Copy and update
The above attempt at a test seems to imply that we're lacking a way to vary a single field while keeping the rest constant. Taking a cue from functional languages, we can introduce a way to do that on the Quote
class itself:
public Quote WithClose(decimal newClose)
{
return new Quote(
_tradingDate,
_open,
_high,
_low,
newClose,
_closeAdjusted,
_volume);
}
This sort of API tends to be very useful on Value Objects, to the point where I always add such methods to my Value Objects.
Let's do the same with Symbol
:
public Symbol WithHistoricalQuotes(IEnumerable<Quote> newHistoricalQuotes)
{
return new Symbol(_identifier, newHistoricalQuotes);
}
This makes it much easier to ask AutoFixture to deal with all the stuff you don't care about while explicitly stating only that which you care about.
Testing with AutoFixture
The original test can now be rewritten as:
[Fact]
public void PriceUnder50()
{
var fixture = new Fixture();
var quotes = new[]
{
fixture.Create<Quote>().WithClose(49),
fixture.Create<Quote>().WithClose(51),
fixture.Create<Quote>().WithClose(50),
fixture.Create<Quote>().WithClose(10),
};
var symbol = fixture.Create<Symbol>().WithHistoricalQuotes(quotes);
var indicator = fixture.Create<UnderPriceIndicator>().WithLimit(50);
var actual = indicator.Apply(symbol);
var expected = new[] { quotes[0], quotes[3] };
Assert.Equal(expected, actual);
}
This test only states those parts of the test case that you care about, while AutoFixture takes care of all the other values that don't have any impact on the test case. This makes the test more robust, as well as more readable.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With