I always see comments about how a Unit Test should only be testing one piece of functionality. For the most part, I definitely agree with that assessment. However, I'm working on testing a method right now that has me pondering if being laxer on this ideal would be a better thing, so I'm curious to everyone's opinions.
The method that I'm testing is one that gathers Messages created between a user-defined date range, and groups them by Date, with totals of each type of Message per day. (Request, Offer, Dialog, and Referral).
So for a range of two days (for simplicity), I'll return an IEnumerable<MessageDay>
, where MessageDay
is defined as such:
public class MessageDay() {
public DateTime Date { get; set; }
public int Requests { get; set; }
public int Offers { get; set; }
public int Dialogs { get; set; }
public int Referrals { get; set; }
public int Total {
get {
return this.Requests + this.Offers + this.Dialogs + this.Referrals;
}
}
}
I have my Mock setup for my unit test with several of each type of Message, ranging from DateTime.MinValue, DateTime.Now, and DateTime.MaxValue.
Should I create a Unit Test with the same parameters going into the method I'm testing repeatedly? (i.e. first test will test a count of the days, second test will test the total Requests for day 1, etc for each type of Message, for each date)
I'm thinking that having so many tests (up to 13 per date range with 4 different date parameter possibilities) would be ridiculous, however I would like to know the standard, accepted way to do this.
EDIT Here is my Mock data that I have set up:
[TestInitialize()]
public void TestInitialize() {
var mock = new Mock<IReportingRepository>();
var now = DateTime.Now;
mock.Setup(m => m.Messages).Returns(new Message[] {
new Request { CreatedOn = now },
new Request { CreatedOn = DateTime.MinValue },
new Request { CreatedOn = DateTime.MaxValue },
new Offer { CreatedOn = DateTime.MinValue },
new Offer { CreatedOn = DateTime.MinValue },
new Offer { CreatedOn = DateTime.MaxValue },
new Dialog { CreatedOn = now },
new Dialog { CreatedOn = DateTime.MaxValue },
new Referral { CreatedOn = now },
new Referral { CreatedOn = DateTime.MinValue }
}.AsQueryable());
controller = new ReportingController(mock.Object);
}
This would be along the lines of my "fewest number of tests":
[TestMethod]
public void CanFindAllCounts() {
var result = (MessageResponse)controller.GetMessageAnalytics(new MessageRequest()).Data;
Assert.AreEqual(3, result.Days.Count());
var day = result.Days.FirstOrDefault();
Assert.AreEqual(4, day.Total);
Assert.AreEqual(1, day.Requests);
Assert.AreEqual(2, day.Offers);
Assert.AreEqual(0, day.Dialogs);
Assert.AreEqual(1, day.Referrals);
day = result.Days.Where(d => d.Date.Date == DateTime.Now.Date).FirstOrDefault();
Assert.AreEqual(3, day.Total);
Assert.AreEqual(1, day.Requests);
Assert.AreEqual(0, day.Offers);
Assert.AreEqual(1, day.Dialogs);
Assert.AreEqual(1, day.Referrals);
day = result.Days.Last();
Assert.AreEqual(3, day.Total);
Assert.AreEqual(1, day.Requests);
Assert.AreEqual(1, day.Offers);
Assert.AreEqual(1, day.Dialogs);
Assert.AreEqual(0, day.Referrals);
}
The other extreme would of course be a test for each of those Asserts. Being that I'm using Moq, it's not a huge deal and would run quickly, but I just feel that it's overkill.
.Equals()
method on your Day class. \$\endgroup\$.Equals()
overkill, because it's part ofSystem.Object
. You have one, but it isn't useful for comparisons at the moment, because it only checks reference equality. It wouldn't be much of a stretch to imagine calling it in the future. \$\endgroup\$TestInitialize
runs just before andTestMethod
runs just after midnight. ReadDateTime.Now
just once and use the result (or some constant value) in both setup and execution of the test. \$\endgroup\$Equals
comment. Even more so becauseEquals
declares something about the design of that class. It is not merely a mechanism to DRY up test code. AND of courseEquals
must be tested too. \$\endgroup\$