有人可以就如何避免这个陷阱给出好的建议吗?
这是一个典型的情况.我设计一个从给定的文本值生成一个小工具的小部件.它总是开始很简单,直到我进入细节.我的小部件必须与几个难以测试的东西进行交互,比如文件系统,数据库和网络.
所以,而不是将所有这些设计到我的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
希望这可以帮助.