我应该如何在C#中对多个必填字段进行单元测试?

时间:2016-12-19 18:09:20

标签: c# unit-testing

想象一下这个简单的场景:我有一个名为MyModel的课程:

public class MyModel
{
    public string Prop01 { get; set; }
    public string Prop02 { get; set; }
    public string Prop03 { get; set; }

    public bool IsValid()
    {
        if (String.IsNullOrEmpty(Prop01) || String.IsNullOrEmpty(Prop02) || String.IsNullOrEmpty(Prop03))
            return false;

        return true;
    }
}

您可以看到,如果MyModel上的任何属性为空或为空,IsValid()方法将返回false,换句话说,所有字段都是“必需的” ”

我写了一些单元测试来测试IsValid()方法:

[TestMethod]
public void MyModel_Invalid_When_Prop01_Is_Null()
{
    var myModel = new MyModel();
    Assert.AreEqual(myModel.IsValid(), false);
}

[TestMethod]
public void MyModel_Invalid_When_Prop02_Is_Null()
{
    var myModel = new MyModel();
    Assert.AreEqual(myModel.IsValid(), false);
}

[TestMethod]
public void MyModel_Invalid_When_Prop03_Is_Null()
{
    var myModel = new MyModel();
    Assert.AreEqual(myModel.IsValid(), false);
}

当然所有这些测试都会通过,但我对此并不满意。让我们想象一下,我是一名看到MyModel_Invalid_When_Prop01_Is_Null测试的开发人员(由另一位开发人员撰写)。我希望通过为Prop01的{​​{1}}赋值,测试应该开始失败。但当然不会发生这种情况,所以我将测试改为:

myModel

现在测试真的测试每个属性都已填充,但是如果我向[TestMethod] public void MyModel_Invalid_When_Prop01_Is_Null() { var myModel = new MyModel(); myModel.Prop02 = "Some value"; myModel.Prop03 = "Some value"; Assert.AreEqual(myModel.IsValid(), false); } [TestMethod] public void MyModel_Invalid_When_Prop02_Is_Null() { var myModel = new MyModel(); myModel.Prop01 = "Some value"; myModel.Prop03 = "Some value"; Assert.AreEqual(myModel.IsValid(), false); } [TestMethod] public void MyModel_Invalid_When_Prop03_Is_Null() { var myModel = new MyModel(); myModel.Prop01 = "Some value"; myModel.Prop02 = "Some value"; Assert.AreEqual(myModel.IsValid(), false); } 添加Prop04,这也是必填字段,我需要再次更改我的所有单元测试,所以我认为这不是一个好主意。

所以我的问题是:我如何以一种我确定测试要么通过还是失败的方式对多个必需属性进行单元测试,因为我正在测试该特定属性?或者也许,我应该测试那些场景吗?

2 个答案:

答案 0 :(得分:5)

您可以从有效的模型开始,然后使其无效,而不是从空的无效模型开始。这将确保您只需要在一个地方修改您的测试,这很好,因为您的实际需求已经改变。

另一个优势是您的测试变得更加明确,因为设置明确地使您的模型无效:

[TestMethod]
public void MyModel_Invalid_When_Prop01_Is_Null()
{
    var myModel = getValidModel();
    myModel.Prop01 = null;    
    Assert.AreEqual(myModel.IsValid(), false);
}

[TestMethod]
public void MyModel_Invalid_When_Prop02_Is_Null()
{
    var myModel = getValidModel();
    myModel.Prop02 = null;

    Assert.AreEqual(myModel.IsValid(), false);
}

[TestMethod]
public void MyModel_Invalid_When_Prop03_Is_Null()
{
    var myModel = getValidModel();
    myModel.Prop03 = null;

    Assert.AreEqual(myModel.IsValid(), false);
}

MyModel getValidModel() => 
    new MyModel
    {
        Prop01 = "Some value",
        Prop02 = "Some value",
        Prop03 = "Some value",
    };

如果模型初始化变得更复杂,则可以使用构建器模式。 我写了一篇关于此的博文,可能会有所帮助:https://www.kenneth-truyers.net/2013/07/15/flexible-and-expressive-unit-tests-with-the-builder-pattern/

答案 1 :(得分:0)

在我看来,如果isValid()足够重要成为一种方法,那么重要的是应该测试它的行为。

在某些方面,其逻辑的简单性可能会让你觉得它不值得所有的工作。好吧,我明白了,但反驳的论点是,如果有人添加了另一个必填字段,则无法更新isValid()是一个非常合理的错误;如果你没有测试isValid()的行为,那么在一些神秘的生产错误之前你不会知道没有人认​​为Prop04是空的,因为嘿,isValid()返回true ... < / p>

所以是的,我会测试它;但是,是的,你可以让这更容易。您可以在测试类中创建一个辅助函数,该函数生成MyModel的虚拟实例,并填充所有字段。然后你的测试方法看起来像

(请原谅任何代码拼写错误;我不是编译器,但我想你会明白我的意思......)

[TestMethod]
public void MyModel_Invalid_When_Prop01_Is_Null()
{
    var myModel = getMyModelInstance();
    myModel.Prop01 = Null;

    Assert.AreEqual(myModel.IsValid(), false);
}

当然,您还有另一种测试方法,将任何属性设置为Null,并断言IsValid()应该返回true。

所以现在你添加一个新属性,你添加一个新的测试方法(你希望它必须做)。您更新帮助程序以填充新属性;无论如何,你需要支持“IsValid()是真的”。因此,其他现有测试方法都不需要更新。