2011-01-05 53 views
4

当我为特定类型的对象编写测试时,例如Forms或UserControls等UI元素,我经常发现自己正在改变我的TDD模式;我定义并布局表单的控件,提供一个“框架”,然后开始编写行为测试(数据绑定/“解除绑定”,显示模式行为等),而不是先进行测试。这样做,我发现自己正在与非公众成员打交道。我也遇到了与其他行为方法相同的担忧;在担心其他方法及其行为的使用之前,我可能想关注并练习由另一种方法调用的某个私人助手的逻辑。对我来说,把所有东西都公开(有时是虚拟的)只是为了能够单元测试一切都是一种气味;我不希望其他对象能够调用助手,或者直接访问文本框;但是我需要知道帮助程序完成它的工作,并且文本框在窗体加载时获得它的值。测试“代理”;好的TDD或代码味道?

我前段时间得出的解决方案是为实际测试的对象创建一个“测试代理”。代理派生自被测对象,并且不会隐藏或覆盖任何行为,但它确实提供内部可见的获取器,设置器和/或调用被测对象的非公共成员的方法,允许我告诉对象执行某些动作,然后我可以查看结果,而不要求测试也依赖于对象内的正确集成,或者仅仅为了测试目的而使该方法或某些其他感兴趣的成员公开于生产代码中。

优点我看:你是否希望有一个单元测试或不

  • 议员的知名度没有被确定。
  • 对测试中的对象进行更好的控制可以实现更灵活和可扩展的测试。

缺点我看到:

  • 类数量的增加,一个额外的水平,只是开发用于测试目的。
  • 必须小心,不能以某种方式结束了在生产中使用的测试代码代理(使构造函数或整个班级内部通常的伎俩)
  • 没有一个“纯粹”的单元测试,你是,在一定程度上,取决于代理和实际被测对象之间的集成。

的问题是,这是建筑师的单元测试有效的办法,还是确实的事实,我必须这样做指示与代码或测试策略有问题?

+0

“私人助手”是一种方法还是一类?如果它是一个班级,那么它的方法应该是公开的,因此可以从外部进行测试 - 这太棒了。如果是一种方法,可以考虑把它变成一个班,从而减少到以前的情况。 – 2011-01-05 18:41:00

+0

好的建议,但不会总是有效。作为一个更具体的例子,我为表单挑选的一个常见模式是实现一个Bind()方法,其作用是获取一些数据对象并填充表单或嵌套控件中包含的控件。这种方法需要操作其他非公共成员(嵌套控件),它只对它所写的一种形式有价值,并且它是系列中正确初始化表单的一个步骤,所以我不希望它被调用直接通过生产代码。 – KeithS 2011-01-05 18:54:59

+0

我错过了什么吗?为了代理你的班级,你是否需要保护私人会员?所以你的测试策略迫使你让你的成员比你想要的更容易访问? – David 2011-03-10 11:22:39

回答

2

我对这种模式的第一反应是你不再强调TDD中的'D'。您的代码已经过测试,但这些测试并未推动您的设计,所以您最终得到的代码与先前编写测试的结果不同。私人状态比必要更难以访问的结构。总的来说,我会争辩说,如果你不能使用公共接口来测试你的类的行为,那么你要么写一个没有意义的测试(测试实现细节),要么你的公共接口设计得不好。

但是,如果你与视图类的工作,因为你必须通过你的观点“公共”的输入和输出要测试,但其不必暴露在使用该视图组件的代码,这一点就更复杂一些。在这种情况下,我认为让您的测试访问该用户界面是有意义的;或者通过将那些通常为私有的属性暴露给测试(您的代理是一种选择,其他可能可用,具体取决于您使用的语言),或者通过编写某种形式的功能测试来驱动UI(再次提供的工具取决于您的环境)。

+0

+!因为“你有一个设计不好的公共界面。” – Raedwald 2011-01-06 17:28:19

+0

我最喜欢这个答案,它可能是“可能不好,但有时可以接受”,这似乎是我的情况;这在我的测试中绝对是罕见的,但有时候并不是另一个好的选择。 – KeithS 2011-01-12 21:15:21

1

我会说这表明您的测试策略或代码存在问题。原因是因为你的违规封装会将你的测试与实现相结合,而不是接口。这会增加您所做的整体工作,因为重构(例如)可能不再免费。

话虽这么说,我觉得有一个很好的理由来破坏封装,和他们围绕副作用功能(通常是与UI编程的情况下)。在许多情况下,您需要确保函数按特定顺序被调用,或者它们被调用。我认为有一些方法可以缓解你违反封装的程度。

如果我正在为副作用编写测试,我通常会将它们分成自己的测试。我也会根据我的要求(即顺序,时间,被调用或不参数等)来存根/模拟副作用函数并声明它们被调用。这使我不知道实现细节,但仍然允许我声明特定函数被正确调用。

在某些语言中,可能很难嘲笑对象或方法。在这些情况下,我将使用依赖注入来传递带有副作用的对象或函数。这样,当测试我可以通过我的模拟验证。

1

MSTest使用此方法来测试私有方法。 Visual Studio将所有测试放在单独的测试项目中,并创建一个“Accessor”类。该课程是所有私人成员都公开的子类。由于该子类在测试项目中,因此它在测试中的组件中不可用。我认为这是测试私有方法的可行模式,如果您不使用Visual Studio和MSTest,可以在TDD环境中手动实现。

+0

+1作为模式参考MS Accessor。但是请注意,OP和您正在谈论单元测试,但不是TDD – 2013-07-12 12:51:54