So I'm new to unit testing (late to the party, but I'm here at least). I'm extracting some common code used by several of my ASP.NET MVC web applications into one or more class libraries. I'm now writing unit tests for some of that code...most of which are actually extension methods on IDataReader (with the intent to be used against an OdbcDataReader). In the code below, the extension method extends object just so I can actually write unit tests. I had tried using Moq to mock IDataReader to test extension methods, but I've read that's not possible. In any case, that's not really what this question is about.
Here is my question. I have a method called "GetSafeDecimal". The intent is that it would be used like:
dr.getSafeDecimal(dr["FieldValue"])
The method is defined like:
public static decimal GetSafeDecimal(this object rdr, object value)
{
decimal result = 0;
decimal temp = -1;
if (value == null || value is DBNull)
result = 0;
else if (decimal.TryParse(value.ToString(), out temp))
result = temp;
else if (value is string)
{
String s = Convert.ToString(value) ?? string.Empty;
s = s.Replace(" ", "");
if (String.IsNullOrEmpty(s) ||
!decimal.TryParse(s, out temp))
{
result = 0;
}
else
{
result = decimal.Parse(s);
}
}
return result;
}
MY actual question: I am seeing a ton of duplication in my unit tests. The setup isn't hard. I'm merely writting one unit test for each input I'm testing. Is this the "correct" way to unit test?
Here are some of my unit tests for the method above:
[TestMethod]
public void GetSafeDecimalNullReturns0()
{
decimal result = "".GetSafeDecimal(null);
Assert.AreEqual(result , 0);
}
[TestMethod]
public void GetSafeDecimalIntReturnsDecimalValue()
{
decimal result = "".GetSafeDecimal(5);
Assert.AreEqual(result, 5);
}
[TestMethod]
public void GetSafeDecimalStringReturns0()
{
decimal result = "".GetSafeDecimal("asdf");
Assert.AreEqual(result, 0);
}
[TestMethod]
public void GetSafeDecimalStringSpecialCharactersReturns0()
{
decimal result = "".GetSafeDecimal("a_ s)@#$df");
Assert.AreEqual(result, 0);
}
[TestMethod]
public void GetSafeDecimalIntStringReturns0()
{
decimal result = "".GetSafeDecimal("2 3 5");
Assert.AreEqual(result, 235);
}
[TestMethod]
public void GetSafeDecimalDecimalReturnsDecimal()
{
decimal result = "".GetSafeDecimal(3.14M);
Assert.AreEqual(result, 3.14M);
}
[TestMethod]
public void GetSafeDecimalDoubleReturnsDecimal()
{
decimal result = "".GetSafeDecimal(3.14d);
Assert.AreEqual(result, 3.14M);
}
@Sebastian's solution was spot on. After switching to nunit, my tests now look like:
public class SafeDecimalTestData
{
public static IEnumerable TestCases
{
get
{
yield return new TestCaseData(null).Returns(0);
yield return new TestCaseData(5).Returns(5);
yield return new TestCaseData("asdf").Returns(0);
yield return new TestCaseData("a_ s)@#$df").Returns(0);
yield return new TestCaseData("2 3 5").Returns(235);
yield return new TestCaseData(3.14m).Returns(3.14m);
yield return new TestCaseData(3.14m).Returns(3.14d);
yield return new TestCaseData(new DateTime(2015,1,1)).Returns(0);
}
}
}
[Test, TestCaseSource(typeof(SafeDecimalTestData), "TestCases")]
public decimal GetSafeDecimalTestInputs(object input)
{
return "".GetSafeDecimal(input);
}
I wrote the tests in this manner instead of just using the TestCase attribute because the tests weren't letting me use values like 3.14d, 3.14m, or DateTime. I wanted a solution that I could use consistently for all my tests regardless the kind of data I was testing. This solution came from the NUnit docs at: