关于不同的输入,我的单元测试应该有多冗余?

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 文档:

TestCaseSourceAttribute

在许多单元测试框架中,有一种特殊的结构允许您定义一个测试方法并提供多个测试用例。例如,在 NUnit 中你有 TestCase 属性。