如何在单元测试中避免多个断言?

Chr*_*cht 11 c# unit-testing assert

这是我第一次尝试进行单元测试,所以请耐心等待.
我仍在尝试对将POCO列表转换为ADO.Recordsets的库进行单元测试.

现在,我正在尝试编写一个创建a的测试List<Poco>,将其转换为Recordset(使用我想测试的方法),然后检查它们是否包含相同的信息(如,Poco.Foo == RS.Foo等等......) .

这是POCO:

public class TestPoco
{
    public string StringValue { get; set; }
    public int Int32Value { get; set; }
    public bool BoolValue { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

...到目前为止这是测试(我正在使用xUnit.net):

[Fact]
public void TheTest()
{
    var input = new List<TestPoco>();
    input.Add(new TestPoco { BoolValue = true, Int32Value = 1, StringValue = "foo" });

    var actual = input.ToRecordset();

    Assert.Equal(actual.BoolValue, true);
    Assert.Equal(actual.Int32Value, 1);
    Assert.Equal(actual.StringValue, "foo");
}
Run Code Online (Sandbox Code Playgroud)

我不喜欢这个是最后的三个断言,POCO的每个属性一个.
我已多次读过一次测试中的多个断言是邪恶的(我理解其中的原因,我同意).

问题是,我该怎样摆脱它们?

我有Roy Osherove的优秀着作"单位测试的艺术"就在我的面前,他有一个例子,它涵盖了这一点(对于那些有本书的人:第7.2.6章,第202/203页):

在他的示例中,测试中的方法返回一个AnalyzedOutput具有多个属性的对象,并且他想断言所有属性以检查每个属性是否包含期望值.

在这种情况下的解决方案:
创建另一个AnalyzedOutput实例,用期望值填充它并断言它是否等于被测试方法返回的那个(并覆盖Equals()以便能够这样做).

但我认为在我的情况下我不能这样做,因为我要测试的方法返回一个ADODB.Recordset.

并且为了创建Recordset具有期望值的另一个,我首先需要从头开始创建它:

// this probably doesn't actually compile, the actual conversion method 
// doesn't exist yet and this is just to show the idea

var expected = new ADODB.RecordsetClass();
expected.Fields.Append("BoolValue", ADODB.DataTypeEnum.adBoolean);
expected.Fields.Append("Int32Value", ADODB.DataTypeEnum.adInteger);
expected.Fields.Append("StringValue", ADODB.DataTypeEnum.adVarWChar);

expected.AddNew();
expected.BoolValue = true;
expected.Int32Value = 1;
expected.StringValue = "foo";
expected.Update();
Run Code Online (Sandbox Code Playgroud)

我也不喜欢这样,因为这基本上是实际转换方法(测试中的方法)中某些代码的重复,这是测试中要避免的另一件事.

那么......我现在能做什么?
在这种特殊情况下,这种重复级别是否仍然可以接受,或者有更好的方法来测试它?

Chr*_*ain 9

我认为,根据事物的精神,这很好.如果我没记错的话,多个断言是"邪恶的"的原因是它暗示你在一次测试中测试多个东西.在这种情况下,您确实这样做是因为您正在测试每个字段,可能是为了确保它适用于多种不同类型.既然这都是对象平等测试,那么我认为你是明确的.

如果你真的想要激动它,每个属性写一个测试(j/k!)


Dan*_*ann 7

在我的书中,每个单元测试的多个断言完全没问题,只要多个断言都声明了相同的测试条件.在您的情况下,他们正在测试转换是否成功,因此测试传递是以所有这些断言为真为条件的.结果,它非常好!

我将"每个测试的一个断言"分类为指导,而不是一个严格的规则.当你忽略它时,请考虑为什么你无视它.

也就是说,解决这个问题的方法是创建一个单独的测试类,在类设置中运行测试过程.然后每个测试只是一个属性的断言.例如:

public class ClassWithProperities
{
    public string Foo { get; set; }
    public int Bar { get; set; }
}

public static class Converter
{
    public static ClassWithProperities Convert(string foo, int bar)
    {
        return new ClassWithProperities {Foo=foo, Bar=bar};
    }
}
[TestClass]
public class PropertyTestsWhenFooIsTestAndBarIsOne
{
    private static ClassWithProperities classWithProperties;

    [ClassInitialize]
    public static void ClassInit(TestContext testContext)
    {
        //Arrange
        string foo = "test";
        int bar = 1;
        //Act
        classWithProperties = Converter.Convert(foo, bar);
        //Assert
    }

    [TestMethod]
    public void AssertFooIsTest()
    {
        Assert.AreEqual("test", classWithProperties.Foo);
    }

    [TestMethod]
    public void AssertBarIsOne()
    {
        Assert.AreEqual(1, classWithProperties.Bar);
    }
}

[TestClass]
public class PropertyTestsWhenFooIsXyzAndBarIsTwoThousand
{
    private static ClassWithProperities classWithProperties;

    [ClassInitialize]
    public static void ClassInit(TestContext testContext)
    {
        //Arrange
        string foo = "Xyz";
        int bar = 2000;
        //Act
        classWithProperties = Converter.Convert(foo, bar);
        //Assert
    }

    [TestMethod]
    public void AssertFooIsXyz()
    {
        Assert.AreEqual("Xyz", classWithProperties.Foo);
    }

    [TestMethod]
    public void AssertBarIsTwoThousand()
    {
        Assert.AreEqual(2000, classWithProperties.Bar);
    }
}
Run Code Online (Sandbox Code Playgroud)