关于不同的输入,我的单元测试应该有多冗余?
How redundant should my unit tests be regarding varied input?
所以我是单元测试的新手(聚会迟到了,但至少我在这里)。我正在将我的几个 ASP.NET MVC Web 应用程序使用的一些通用代码提取到一个或多个 class 库中。我现在正在为其中一些代码编写单元测试......其中大部分实际上是 IDataReader 上的扩展方法(旨在用于 OdbcDataReader)。在下面的代码中,扩展方法扩展了对象,这样我就可以实际编写单元测试了。我曾尝试使用 Moq 来模拟 IDataReader 来测试扩展方法,但我读过这是不可能的。无论如何,这不是这个问题的真正含义。
这是我的问题。 我有一个方法叫做 "GetSafeDecimal"。目的是像这样使用它:
dr.getSafeDecimal(dr["FieldValue"])
方法定义如下:
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;
}
我的实际问题:我在单元测试中看到大量重复。设置并不难。我只是为我正在测试的每个输入编写一个单元测试。这是单元测试的 "correct" 方式吗?
以下是我对上述方法的一些单元测试:
[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 的解决方案恰到好处。切换到 nunit 后,我的测试现在看起来像:
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);
}
我以这种方式编写测试,而不是仅使用 TestCase 属性,因为测试不允许我使用 3.14d、3.14m 或 DateTime 等值。我想要一个解决方案,无论我测试的数据类型如何,我都可以在所有测试中始终如一地使用它。此解决方案来自 NUnit 文档:
在许多单元测试框架中,有一种特殊的结构允许您定义一个测试方法并提供多个测试用例。例如,在 NUnit 中你有 TestCase 属性。
所以我是单元测试的新手(聚会迟到了,但至少我在这里)。我正在将我的几个 ASP.NET MVC Web 应用程序使用的一些通用代码提取到一个或多个 class 库中。我现在正在为其中一些代码编写单元测试......其中大部分实际上是 IDataReader 上的扩展方法(旨在用于 OdbcDataReader)。在下面的代码中,扩展方法扩展了对象,这样我就可以实际编写单元测试了。我曾尝试使用 Moq 来模拟 IDataReader 来测试扩展方法,但我读过这是不可能的。无论如何,这不是这个问题的真正含义。
这是我的问题。 我有一个方法叫做 "GetSafeDecimal"。目的是像这样使用它:
dr.getSafeDecimal(dr["FieldValue"])
方法定义如下:
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;
}
我的实际问题:我在单元测试中看到大量重复。设置并不难。我只是为我正在测试的每个输入编写一个单元测试。这是单元测试的 "correct" 方式吗?
以下是我对上述方法的一些单元测试:
[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 的解决方案恰到好处。切换到 nunit 后,我的测试现在看起来像:
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);
}
我以这种方式编写测试,而不是仅使用 TestCase 属性,因为测试不允许我使用 3.14d、3.14m 或 DateTime 等值。我想要一个解决方案,无论我测试的数据类型如何,我都可以在所有测试中始终如一地使用它。此解决方案来自 NUnit 文档:
在许多单元测试框架中,有一种特殊的结构允许您定义一个测试方法并提供多个测试用例。例如,在 NUnit 中你有 TestCase 属性。