3

我有一个简单的方法可以从集合中计算总数。

public void MethodToTest(Collection<int> collection)
{
    int sum = 0;
    foreach (int value in collection)
    {
        sum += value;
    }
}

目标是使用命令行中运行的 opencoverage 工具获得 100% 的分支覆盖率。我还有一个调用 MethodToTest 方法的单元测试:

[TestMethod]
public void TestMethodToTest()
{
    BomProviderMock mock = new BomProviderMock();
    BomManager bomManager = new BomManager(mock);

    List<int> list = new List<int>();
    for (int i = 0; i <= Int16.MaxValue; i++)
    {
        list.Add(i);
    }
    // Firts attempt with a non empty collection
    bomManager.MethodToTest(new Collection<int>(list));

    // Second attempt with an empty collection
    bomManager.MethodToTest(new Collection<int>());
}

使用工具 opencover 后,MethodToTest 方法在分支覆盖率上获得了 80%。我的问题是,foreach 循环是否会影响分支覆盖率,如果是,我怎样才能用这个简单的代码获得 100% 的覆盖率?

4

2 回答 2

5

正如[原始]接受的答案所指出的那样,您的实际情况减少到collection.Sum()但是您将无法每次都摆脱这种情况。

如果我们使用 TDD 来开发这个(我同意过度杀伤,但很容易解释),我们将 [可能] 执行以下操作(出于偏好,我在此示例中也使用 NUnit)

[Test]
public void Sum_Is_Zero_When_No_Entries()
{
    var bomManager = new BomManager();
    Assert.AreEqual(0, bomManager.MethodToTest(new Collection<int>()));
}

然后编写以下代码(注意:我们编写满足当前测试集的最小值)

public int MethodToTest(Collection<int> collection)
{
    var sum = 0;
    return sum;
}

然后我们将编写一个新的测试,例如

[Test]
[TestCase(new[] { 0 }, 0)]
public void Sum_Is_Calculated_Correctly_When_Entries_Supplied(int[] data, int expected)
{
    var bomManager = new BomManager();
    Assert.AreEqual(expected, bomManager.MethodToTest(new Collection<int>(data)));
}

如果我们运行我们的测试,它们都会通过(绿色)所以我们需要一个新的测试(案例)

[TestCase(new[] { 1 }, 1)]
[TestCase(new[] { 1, 2, 3 }, 6)]

为了满足这些测试,我需要修改我的代码,例如

public int MethodToTest(Collection<int> collection)
{
    var sum = 0;
    foreach (var value in collection)
    {
        sum += value;
    }
    return sum;
}

现在我所有的测试都工作了,如果我通过 opencover 运行它,我将获得 100% 的序列和分支覆盖率 - 万岁!.. 我这样做时没有使用覆盖率作为我的控制,而是编写了正确的测试来支持我的代码。

但是有一个“可能的”缺陷......如果我通过了null怎么办?是时候进行新的测试了

[Test]
public void Sum_Is_Zero_When_Null_Collection()
{
    var bomManager = new BomManager();
    Assert.AreEqual(0, bomManager.MethodToTest(null));
}

测试失败,所以我们需要更新我们的代码,例如

public int MethodToTest(Collection<int> collection)
{
    var sum = 0;
    if (collection != null)
    {
        foreach (var value in collection)
        {
            sum += value;
        }
    }
    return sum;
}

现在我们有支持我们代码的测试而不是测试我们的代码的测试,即我们的测试不关心我们如何编写代码。

现在我们有了一组很好的测试,所以我们现在可以安全地重构我们的代码,例如

public int MethodToTest(IEnumerable<int> collection)
{
    return (collection ?? new int[0]).Sum();
}

我这样做并没有影响任何现有的测试。

我希望这有帮助。

于 2014-10-01T23:45:28.637 回答
2

I've Updated your question a bit, using some linq instead of the foreach loops. It takes a random number (same size list though), so the compiler won't "whisk it away" and have to compute it.

I would suggest doing something with the sum in the method, or returning it, it might change your results.

Last, but not least, don't obsess about 100%. It'll never happen in a real life big project. Just make sure you test the things that might brake, and build your software with testing in mind, so it'll be easy to do.

void Main()
{
    Random r = new Random();
    List<int> list = Enumerable.Range(1,Int16.MaxValue)
                               .Select (e => r.Next(0,Int16.MaxValue))
                               .ToList();

    // Firts attempt with a non empty collection
    MethodToTest(new Collection<int>(list));

    // Second attempt with an empty collection
    MethodToTest(new Collection<int>());
}

// Define other methods and classes here
public void MethodToTest(Collection<int> collection)
{
    var sum = collection.Sum (i => i);
    // do something with it. If you're just voiding it and it doesn't get 
    // used, it might be removed by compiler.
}
于 2014-09-30T22:00:09.430 回答