代码之家  ›  专栏  ›  技术社区  ›  OwenP

您对产生复杂对象图的测试方法做了什么?

  •  1
  • OwenP  · 技术社区  · 15 年前

    我是一个控件开发人员和一个相对新的单元测试人员。几乎每天,我都反对这种由于用户界面交互而无法测试控件的态度。我正在生成一个演示控件,以证明如果控件设计为可测试的,则可以显著减少手动测试。目前我有50%的逻辑覆盖率,但我认为如果我能找到一种方法来测试一些更复杂的部分,我可以将其提高到75%或更高。

    例如,我有一个具有描述控件状态的属性的类和一个生成WPF的方法 PathGeometry 由若干段组成的对象。实现如下所示:

    internal PathGeometry CreateOuterGeometry()
    {
        double arcRadius = OuterCoordinates.Radius;
        double sweepAngle = OuterCoordinates.SweepAngle;
        ArcSegment outerArc = new ArcSegment(...);
    
        LineSegment arcEndToCenter = new LineSegment(...);
    
        PathFigure fig = new PathFigure();
        // configure figure and add segments...
    
        PathGeometry outerGeometry = new PathGeometry();
        outerGeometry.Figures.Add(fig);
        return outerGeometry;
    }
    

    我还有一些其他的方法,比如说,它们占据了几百块未覆盖的代码,额外的25%的覆盖率。我最初计划测试这些方法,但拒绝了这个概念。我仍然是一个单元测试新手,我能想到的测试代码的唯一方法是以下几种方法:

    void CreateOuterGeometry_AngleIsSmall_ArcSegmentIsCorrect()
    {
        ClassUnderTest classUnderTest = new ClassUnderTest();
        // configure the class under test...
        ArcSegment expectedArc = // generate expected Arc...
    
        PathGeometry geometry = classUnderTest.CreateOuterGeometry()
        ArcSegment arc = geometry.Figures.Segments[0];
    
        Assert.AreEqual(expectedArc, arc)
    }
    

    测试本身看起来很好;我将为每个预期的段编写一个。但我有一些问题:

    • 是否需要测试来验证“第一段是 ArcSegment ?“理论上,测试测试这个,但是每个测试不应该只测试一件事吗?这听起来像两件事。
    • 控件至少有六个用于计算的案例和四个边缘案例;这意味着对于每个方法,我至少需要十个测试。
    • 在开发过程中,我多次更改了各种几何图形的生成方式。这将导致我不得不重写所有的测试。

    第一个问题让我停顿了一下,因为它似乎会夸大测试的数量。我想我可能需要测试“有X段吗?”“n段是正确的类型吗?”,但是现在我已经考虑了更多,我发现方法中没有分支逻辑,所以我只需要做一次这些测试。第二个问题使我更加确信,与测试相关的工作将会非常多。这似乎是不可避免的。第三个问题使前两个问题复杂化。每次我改变几何体的计算方式时,我都要编辑估计的40个测试,以使它们尊重新的逻辑。这还包括在添加或删除段时添加或删除测试。

    由于这三个问题,我选择编写一个应用程序和手动测试计划,将控件置于所有有趣的状态,并要求用户验证它看起来是一种特殊的方式。这是错的吗?我是否高估了编写单元测试所涉及的工作?是否有其他方法可以更容易地测试这一点?(我目前正在研究mock和stub;它似乎需要对设计进行一些重构,最终得到的效果差不多是如此。)

    6 回复  |  直到 15 年前
        1
  •  2
  •   Samuel Carrijo    15 年前

    使用依赖注入和模拟。

    为ArcSegmentFactory、LineSegmentFactory等创建接口,并将模拟工厂传递给类。这样,你就可以 隔离特定于此对象的逻辑 (这将使测试更容易),并且不会依赖于其他对象的逻辑。

    关于测试内容: 你应该测试什么是重要的 . 你可能有一个时间线,在其中你想做的事情,你可能不会能够测试每一件事。 确定你需要测试的东西的优先级 ,并按优先级进行测试(考虑测试需要多少时间)。另外,当您已经做了一些测试后,为其他东西创建新的测试会变得更容易,而且我在为同一个类创建多个测试时并没有真正看到问题…

    关于变化,这就是测试的目的:允许你改变,不要真的害怕你的改变会给世界带来混乱。

        2
  •  0
  •   Larry Watanabe    15 年前

    您可以尝试编写一个生成随机控制图的控件生成工具,并对其进行测试。这可能会产生一些您可能没有想到的数据点。

        3
  •  0
  •   RMorrisey    15 年前

    在我们的项目中,我们使用JUnit来执行严格来说不是单元测试的测试。例如,我们发现挂接一个空白数据库并将Hibernate(对象关系映射工具)生成的自动模式与测试数据库的实际模式进行比较是很有帮助的;这有助于我们捕获错误的数据库映射的许多问题。但总的来说…在给定的测试方法中,您应该只在一个类上测试一个方法。这并不意味着您不能对它进行多个断言来检查对象的各种属性。

        4
  •  0
  •   Aaron Digulla    15 年前

    我的方法是将图表转换为字符串(每行一段),并将此字符串与预期结果进行比较。

    如果更改代码中的某些内容,测试将开始失败,但您需要做的只是检查故障是否在正确的位置。您的IDE应该为此提供一个并排的差异。

    当您确信新输出是正确的时,只需将其复制到旧的预期结果上。这将确保一个错误不会被忽视(至少不会持续很长时间),测试仍然很简单,并且可以快速修复。

    接下来,如果您有公共路径部分,那么您可以将它们放入单独的字符串中,并从这些部分构建测试的预期结果。这允许您避免重复您自己(如果公共部分发生更改,您只需为所有测试更新一个位置)。

        5
  •  0
  •   Galghamon    15 年前

    如果我正确理解了您的示例,那么您正试图找到一种方法来测试一组绘图操作是否产生给定的结果。

    您可以生成一组预期图像(已验证的“良好”图像的快照),并创建单元测试,使用绘图操作创建相同的图像集,并将结果与图像比较进行比较。这将允许您自动化图形操作的测试,这就是我理解您的问题所在。

        6
  •  -1
  •   tster    15 年前

    要做到这一点,教科书中的方法是将所有业务逻辑移动到库或控制器,这些库或控制器由GUI中的一行方法调用。这样,您就可以在不处理GUI的情况下对控制器或库进行单元测试。