When applying TDD, do you create tests that verify expected excepti开发者_开发问答ons for arguments (ArgumentException, ArgumentNullException, InvalidOperation, etc.) or just ones that are "known", for example, CustomerDelinquentException ?
What about equals, gethashcode overrides? how would i test gethashcode?
Thanks
I always test for any exceptions I throw in my method, including ArgumentNullException
, ArgumentException
, etc. I just find it's best to test these and they're easy to write. That way if someone ever removes those guards, the tests would break and you'd know.
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void ToSetOnNullThrows()
{
List<string> list = null;
var target = list.ToHashSet();
}
As far as GetHashCode()
and Equals()
i test these as well if you override them. For GetHashCode()
an easy test is to create two equivalent objects (same values used in the hash code) and prove that the hash codes both objects generate are the same.
[TestMethod]
public void GetHashCodeSameKeysAreSameTest()
{
var key = new CompositeKey<string, int>("A", 13);
var otherKey = new CompositeKey<string, int>("A", 13);
Assert.AreEqual(key.GetHashCode(), otherKey.GetHashCode());
}
You can try to test that two non-equivalent objects return different hash codes, but you'd have to make sure the values you use aren't simply a collision. This largely depends on the algorithm you code in GetHashCode()
.
[TestMethod]
public void GetHashCodeDifferentKeysAreMostLikelyDifferentTest()
{
var key = new CompositeKey<string, int>("A", 13);
var otherKey = new CompositeKey<string, int>("A", 14);
Assert.AreNotEqual(key.GetHashCode(), otherKey.GetHashCode());
}
For Equals() test that two equivalent objects with the same fields return true
on Equals()
and false
on two non-equivalent objects.
[TestMethod]
public void EqualsTest()
{
var key = new CompositeKey<string, int>("A", 13);
var otherKey = new CompositeKey<string, int>("A", 13);
Assert.IsTrue(key.Equals(otherKey));
}
[TestMethod]
public void NotEqualsTest()
{
var key = new CompositeKey<string, int>("A", 13);
var otherKey = new CompositeKey<string, int>("A", 15);
Assert.IsFalse(key.Equals(otherKey));
}
For even more fun, I like to unit tests DateTime
dependent stuff too. This is somewhat harder, but if a method's behavior depends on DateTime
i still want to unit test them. So you can create a DateTime
generator delegate that defaults to returning DateTime.Now
but have it so that you can set the generator to a specific DateTime
. Helps a lot with coverage in my line of work since I'm in the financial industry and a lot of logic depends on pre-market, post-market hours, etc...
public class SomeClassThatDependsOnCurrentTime
{
internal Func<DateTime> NowGenerator { get; set; }
public SomeClassThatDependsOnCurrentTime()
{
// default in constructor to return DateTime.Now
NowGenerator = () => DateTime.Now;
}
public bool IsAfterMarketClose()
{
// call the generator instead of DateTime.Now directly...
return NowGenerator().TimeOfDay > new TimeSpan(16, 0, 0);
}
}
Then you just set up a unit test to inject a specific date time.
If your code tests arguments and throws exceptions, then yes, you should test for those exceptions. If not or in addition, you should test your code with invalid values to see what happens.
You should test for equals as well, that kind of tests gethashcode too, when you expect two objects to be equal.
Yes. Don't forget that unit tests serves as a form of documentation too. It shows to the next developer or clients using your component the preconditions of the function or method they are calling.
精彩评论