我是一名控制开发人员,也是单元测试的新手。几乎每天,我都会因为UI交互而无法测试控件的态度。我正在制作一个演示控件,表明如果控件设计为可测试的话,可以大大减少手动测试。目前我有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段吗?”之类的东西。并且“段是否是正确的类型?”,但是现在我已经想到了更多我看到方法中没有分支逻辑所以我只需要做一次这些测试。第二个问题使我更加确信测试会有很多努力。这似乎是不可避免的。第三个问题是前两个问题。每次我改变计算几何的方式时,我都要编辑估计的40个测试,以使它们尊重新的逻辑。这还包括在添加或删除段时添加或删除测试。
由于这三个问题,我选择编写一个应用程序和手动测试计划,将控件置于所有有趣的状态,并要求用户验证它看起来是一种特定的方式。这是错的吗?我是否高估了编写单元测试所需的工作量?有没有其他方法来测试这可能更容易? (我目前正在研究模拟和存根;看起来它需要对设计进行一些重构,并最终获得大致的努力。)
答案 0 :(得分:2)
使用依赖注入和模拟。
为ArcSegmentFactory,LineSegmentFactory等创建接口,并将模拟工厂传递给您的类。这样,您将隔离特定于此对象的逻辑(这应该使测试更容易),并且不会依赖于其他对象的逻辑。
关于测试内容:您应该测试什么是重要的。你可能有一个时间表,你想要完成任务,你可能无法测试每一件事。 优先考虑您需要测试的内容,并按优先级顺序进行测试(考虑测试需要多长时间)。此外,当您已经进行了一些测试时,为其他内容创建新测试变得更加容易,而且我没有真正看到为同一个类创建多个测试的问题...
关于这些变化,这就是测试的目的:允许你改变而不是真的害怕你的改变会给世界带来混乱。
答案 1 :(得分:0)
您可以尝试编写生成随机控制图的控件生成工具,并对其进行测试。这可能会产生一些您可能没有想过的数据点。
答案 2 :(得分:0)
在我们的项目中,我们使用JUnit来执行严格来说不是单元测试的测试。例如,我们发现,连接空白数据库并将Hibernate(对象关系映射工具)生成的自动模式与测试数据库的实际模式进行比较是有帮助的。这有助于我们捕获错误的数据库映射的许多问题。但总的来说......你应该只在一个类中测试给定测试方法中的一个方法。这并不意味着你不能对它做多个断言来检查对象的各种属性。
答案 3 :(得分:0)
我的方法是将图形转换为字符串(每行一个段),并将此字符串与预期结果进行比较。
如果您在代码中更改某些内容,测试将开始失败,但您需要做的就是检查失败是否在正确的位置。您的IDE应为此提供并排差异。
当您确信新输出正确时,只需将其复制到旧的预期结果上即可。这将确保不会忽视错误(至少不会长时间),测试仍然很简单,并且可以快速修复。
接下来,如果您有共同的路径部分,那么您可以将它们放入单独的字符串中,并从这些部分构建测试的预期结果。这样可以避免重复自己(如果公共部分发生变化,您只需为所有测试更新一个地方)。
答案 4 :(得分:0)
如果我正确理解你的例子,你试图找到一种方法来测试一大堆绘制操作是否产生给定的结果。
您可以制作一组预期图像(经过验证的“好”图像的快照)而不是人眼,并创建单元测试,使用绘制操作创建相同的图像集并将结果与图像比较。这将允许您自动化图形操作的测试,这是我理解您的问题。
答案 5 :(得分:-1)
教科书的方法是将所有业务逻辑移动到GUI中的1行方法调用的库或控制器。这样,您可以在不处理GUI的情况下对控制器或库进行单元测试。