分支覆盖与foreach?

时间:2014-09-30 21:38:35

标签: c# asp.net visual-studio-2010 opencover

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

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%?

2 个答案:

答案 0 :(得分: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%的序列和分支覆盖 - Hurray!....我没有使用coverage作为我的控件,但编写正确的测试以支持我的代码。

但是有可能的&#39;缺陷...如果我通过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();
}

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

我希望这会有所帮助。

答案 1 :(得分:2)

我稍微更新了你的问题,使用了一些linq而不是foreach循环。它需要一个随机数字(虽然大小相同),因此编译器不会“扫除它”并且必须计算它。

我建议用方法中的总和做一些事情,或者返回它,它可能会改变你的结果。

最后,但并非最不重要的是,不要大约100%。它永远不会发生在现实生活中的大项目中。只要确保你测试可能会制造的东西,并在考虑测试的情况下构建你的软件,这样做很容易。

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.
}