单元测试 – 如何编写没有这么多嘲讽的测试?

前端之家收集整理的这篇文章主要介绍了单元测试 – 如何编写没有这么多嘲讽的测试?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我是一个重要的倡导者,正确的测试驱动设计或行为驱动设计,我喜欢写作测试.然而,我将自己编码到一个角落,我需要在一个特定的测试用例中使用3-5个模拟单个类.无论从哪种方式开始,自上而下或自下而上我最终都需要一个设计,至少需要三个来自最高抽象层次的合作者.

有人可以就如何避免这个陷阱给出好的建议吗?

这是一个典型的情况.我设计一个从给定的文本值生成一个小工具的小部件.它总是开始很简单,直到我进入细节.我的小部件必须与几个难以测试的东西进行交互,比如文件系统,数据库和网络.

所以,而不是将所有这些设计到我的Widget中,我使一个Bridget的合作者. Bridget照顾了复杂性的一半,数据库和网络,让我专注于另一半是多媒体演示.所以,然后我做一个执行多媒体片段的Gidget.整个事情需要在后台发生,所以现在我包括一个Thridget来实现这一点.当我们完成所有的工作并完成后,我最终得到一个Widget,这个Widget手工工作到一个Thidget,这个Thidget会对Bridget进行讨论,将其结果交给Gidget.

因为我正在CocoaTouch工作,并试图避免模拟对象,我使用自我分流模式,其中抽象的协作者成为我测试采用的协议.有3名合作者,我的测试气球变得太复杂了.即使使用像OCMock模拟对象这样的东西,也让我有一种我宁愿避免的复杂性.我试着把我的大脑围绕着一个菊花链的合作者(代表B代表C等等),但我无法想象.

编辑
从下面的示例中我们假设我们有一个必须从套接字读取/写入的对象,并呈现返回的电影数据.

//Assume myRequest is a String param...
InputStream   aIn  = aSocket.getInputStram();
OutputStream  aOut = aSocket.getOutputStram();
DataProcessor aProcessor = ...;

// This gets broken into a "Network" collaborator.
for(stuff in myRequest.charArray()) aOut.write(stuff);
Object Data = aIn.read(); // Simplified read

//This is our second collaborator
aProcessor.process(Data);

现在上面显然处理了网络延迟,所以它必须是Threaded.这引入了一个Thread抽象,使我们脱离了线程单元测试的实践.我们现在有

AsynchronousWorker myworker = getWorker(); //here's our third collaborator
worker.doThisWork( new WorkRequest() {
//Assume myRequest is a String param...
DataProcessor aProcessor = ...;

// Use our "Network" collaborator.
NetworkHandler networkHandler = getNetworkHandler();
Object Data = networkHandler.retrieveData(); // Simplified read

//This is our multimedia collaborator
aProcessor.process(Data);
})

原谅我工作倒退w / o测试,但我要带我的女儿在外面,我正在通过这个例子.这里的想法是,我将协调几个协作者的协作,从一个简单的界面,将绑定到一个UI按钮点击事件.所以最外面的测试反映了一个Sprint任务,给出了一个“播放电影”按钮,当它被点击时,电影将播放.
编辑
来!我们讨论一下.

有很多模拟对象表明:

1)你有太多的依赖.
重新看看你的代码,并试图进一步打破它.特别是尝试分离数据转换和处理.

既然我没有在开发环境方面的经验,所以让我以自己的经验为例.

在Java套接字中,您将获得一组InputStream和OutputStream简单,以便您可以从对象读取数据并发送数据.所以你的程序如下所示:

InputStream  aIn  = aSocket.getInputStram();
OutputStream aOut = aSocket.getOutputStram();

// Read data
Object Data = aIn.read(); // Simplified read
// Process
if (Data.equals('1')) {
   // Do something
   // Write data
   aOut.write('A');
} else {
   // Do something else 
   // Write another data
   aOut.write('B');
}

如果你想测试这种方法,你必须最终为In和Out创建模拟,这可能需要相当复杂的类来支持它们.

但是,如果仔细看一下,从aIn读取并写入aOut可以将其从处理中分离出来.所以你可以创建另一个类,它将读取输入和返回输出对象.

public class ProcessSocket {
    public Object process(Object readObject) {
        if (readObject.equals(...)) {
       // Do something
       // Write data
       return 'A';
    } else {
       // Do something else 
       // Write another data
       return 'B';
   }
}

你以前的方法将是:

InputStream   aIn  = aSocket.getInputStram();
OutputStream  aOut = aSocket.getOutputStram();
ProcessSocket aProcessor = ...;

// Read data
Object Data = aIn.read(); // Simplified read
aProcessor.process(Data);

这样你就可以测试这个处理,而不需要模拟.你测试可以去:

ProcessSocket aProcessor = ...;
assert(aProcessor.process('1').equals('A'));

成为现在独立于输入,输出甚至套接字的处理.

2)您通过单元测试进行单元测试,应进行集成测试.

一些测试不是用于单元测试(在某种意义上说,它需要不必要的更多的努力,并且可能无法有效地获得良好的指标).这些测试的例子是涉及并发和用户界面的测试.它们需要不同的测试方法,而不是单元测试.

我的建议是,你进一步打破它们(类似于上面的技术),直到其中一些是单元测试合适的.所以你有一些难以测试的零件.

编辑

如果你相信你已经把它打碎成很好的部分,也许这就是你的问题.

软件组件或子组件以某种方式相互关联,如字符组合为单词,单词组合到句子,句子到段落,段落到小节,章节,章节等.

我的例子说,你应该把段落和你已经陷入困境的事情分开了.

以这种方式看,大多数时候,段落与其他段落相关,而不是与其他句子相关(或依赖于)其他句子的松散程度.小说部分更宽松,而文字和角色更依赖(如语法规则踢).

所以也许你打破了这么好的语法语法强制这些依赖,反过来强迫你有这么多的模拟对象.

如果是这种情况,您的解决方案是平衡测试.如果一部分依赖于许多部分,并且需要一组复杂的模拟对象(或者简单的更多的努力来测试它).可能你不需要测试它.例如,如果A使用B,C使用B,而B很难测试.那么你为什么不把A B作为一个,C B就是花药.在我的例子中,如果SocketProcessor是如此难以测试,太难了,你将花费更多的时间来测试和维护测试,而不是开发测试,那么它是不值得的,我将一次测试所有的东西.

没有看到你的代码(和事实上,我从来没有开发CocaoTouch),这将很难说.我可以在这里提供很好的评论.对不起D

编辑2
看到你的例子,很明显你正在处理集成问题.假设你已经分开测试播放电影和UI.这是可以理解的,为什么你需要这么多的模拟对象.如果这是第一次使用这种类型的集成结构(这种并发模式),那么这些模拟对象可能实际上是需要的,没有什么可以做的.这就是我可以说的:-p

希望这可以帮助.

猜你在找的设计模式相关文章