单元测试检查边缘情况的发生

时间:2019-05-19 09:31:25

标签: c# .net unit-testing

我具有使用Bresenham算法在网格上进行射线广播的功能。我想针对直线或对角线的特定情况进行优化;当然,这种逻辑不会返回/暴露给用户。

是否可以通过单元测试来检测优化边缘情况?例如。调用函数时,请寻找特定的Debug.WriteLine标记。

我是单元测试的新手,我可能会遗漏要点(向用户公开测试功能),但是断言我的优势案例按预期工作是非常宝贵的,尤其是在积极开发优化时。

我要实现的示例:

public IEnumerable<Coordinate> RayCast (Coordinate source, Coordinate direction) {
    if (direction.X == 0 || direction.Y == 0) {
        Debug.WriteLine ("Orthogonal_Edge_Case");
        //Simple iteration across 1 axis
        ...
        yield break;
    }
    if (Math.Abs(direction.X) == Math.Abs(direction.Y)) {
        Debug.WriteLine ("Diagonal_Edge_Case");
        //Simple diagonal iteration
        ...
        yield break;
    }        
    //Standard Bresenham's algorithm
    ...
    yield break;
}

...

[TestMethod]
public void TestRayCast () {
    var source = new Coordinate (0,0);

    var orthogonal = new Coordinate (0,1);
    CoordinateUtil.RayCast (source, orthogonal);
    //Check that the Orthogonal_Edge_Case marker was emitted

    var diagonal = new Coordinate (1,1);
    CoordinateUtil.RayCast (source, diagonal);
    //Check that the Diagonal_Edge_Case marker was emitted

    //Usual tests for RayCast
    ...
}

注意:我正在使用Visual Studio 2019的单元测试套件,但我很好奇是否可以使用任何.NET工具

2 个答案:

答案 0 :(得分:1)

您有两种选择:

  1. 使用CoordinateUtil中的属性保持某种状态,这将使您能够检查最后检测到的边缘是什么。但是,这会破坏Command Query Responsibility Segregation pattern
  2. 注入一个边缘案例检测器(您可以使用模拟框架轻松地对其进行模拟)。下面的示例使用Moq
  3. 注入ILogger,这将使您可以记录一些计时信息以及其他信息(与option2非常相似,但使用slightly different set up)。

下面显示了选项2的工作方式。您可能会觉得这很过分,但是它很健壮,并且在大型项目中通常是如何完成的。还要注意,您现在将内部组件暴露给外部世界,只是为了帮助内部优化。如果是这样,那可能不合适。

public class CoordinateUtil
{
    private readonly IEdgeCaseDetector edgeCaseDetector;

    // This is the important bit where you inject an edge case detector
    public CoordinateUtil(IEdgeCaseDetector edgeCaseDetector)
    {
        this.edgeCaseDetector = edgeCaseDetector;
    }

    public IEnumerable<Coordinate> RayCast(Coordinate source, Coordinate direction)
    {
        if (direction.X == 0 || direction.Y == 0)
        {
            edgeCaseDetector.Detect("Orthogonal_Edge_Case");
            //Simple iteration across 1 axis
            yield break;
        }
        if (Math.Abs(direction.X) == Math.Abs(direction.Y))
        {
            edgeCaseDetector.Detect("Diagonal_Edge_Case");
            //Simple diagonal iteration
            yield break;
        }

        //Standard Bresenham's algorithm
        yield break;
    }
}

public interface IEdgeCaseDetector
{
    void Detect(string message);
}

public class EdgeCaseDetector
{
    public void Detect(string message)
    {
       // If you wanted to you could simply save the edge cases to a public property here
       // Or you might want to log them when you code runs outside of the unit test
    }
}

[TestClass]
public class CoordinateUtilTests
{
    [TestMethod]
    public void RayCast_WhenOthogonal_DetectsEdgeCase()
    {
        // Arrange
        var mock = new Mock<IEdgeCaseDetector>();
        var coordinateUtil = new CoordinateUtil(mock.Object);
        var source = new Coordinate(1, 1);

        // Act
        // Remember the ToArray because we need to evaluate the enumerable
        // before we can check if the edge case was detected.
        coordinateUtil.RayCast(source, new Coordinate(0, 0)).ToArray();

        // Assert
        mock.Verify(x => x.EdgeDetected("Orthogonal_Edge_Case"));
    }
}

答案 1 :(得分:0)

仅出于测试目的而将内部逻辑暴露给外部世界不是最佳方法。
您应该仅针对公共api和行为进行测试。
对于性能测试,这可能很棘手,但可行。例如,如果对边缘情况执行标准Bresenham算法,则该测试方法的行为使用者将观察到什么?

执行是否需要花费500毫秒以上的时间?如果是这样,请为其编写测试,并检查边缘情况下执行所花费的时间少于500毫秒。

如果找不到这种可观察到的行为,我将不会为其编写测试,而只相信曾经编写或维护此代码的开发人员。所有开发人员都很聪明,会尽力而为。