Unit testing 如何在没有这么多模拟的情况下编写测试?
我大力提倡正确的测试驱动设计或行为驱动设计,我喜欢编写测试。然而,我一直把自己编码到一个角落,我需要在一个特定的测试用例中为单个类使用3-5个模拟。无论我从哪种方式开始,自上而下还是自下而上,我最终的设计都需要来自最高抽象级别的至少三个协作者 有人能就如何避免这个陷阱给出好的建议吗 这是一个典型的场景。我设计了一个小部件,它根据给定的文本值生成一个侏儒。在我进入细节之前,它总是非常简单。我的Widget必须与一些难以测试的东西交互,比如文件系统、数据库和网络 因此,我没有将所有这些都设计到我的小部件中,而是制作了一个Bridget协作器。Bridget处理了复杂性的一半,数据库和网络,这让我可以专注于另一半,即多媒体演示。然后,我做了一个Gidget来表演多媒体片段。整个事情都需要在后台发生,所以现在我加入了一个Thridget来实现这一点。当所有的事情都说了又做了,我最终得到了一个小部件,它将工作交给Thridget,Thridget通过Bridget将结果传递给Gidget 因为我在CocoaTouch中工作,并且试图避免模拟对象,所以我使用了自分流模式,在这种模式中,协作者上的抽象成为我的测试采用的协议。有了3个以上的合作者,我的测试气球变得太复杂了。即使使用OCMock对象这样的东西,也会给我留下一个我宁愿避免的复杂顺序。我试着将我的大脑包围在一个雏菊般的协作链上(a代表B,B代表C等等),但我无法想象 编辑 下面举一个例子,我们假设有一个对象必须从套接字读/写,并显示返回的电影数据Unit testing 如何在没有这么多模拟的情况下编写测试?,unit-testing,tdd,mocking,Unit Testing,Tdd,Mocking,我大力提倡正确的测试驱动设计或行为驱动设计,我喜欢编写测试。然而,我一直把自己编码到一个角落,我需要在一个特定的测试用例中为单个类使用3-5个模拟。无论我从哪种方式开始,自上而下还是自下而上,我最终的设计都需要来自最高抽象级别的至少三个协作者 有人能就如何避免这个陷阱给出好的建议吗 这是一个典型的场景。我设计了一个小部件,它根据给定的文本值生成一个侏儒。在我进入细节之前,它总是非常简单。我的Widget必须与一些难以测试的东西交互,比如文件系统、数据库和网络 因此,我没有将所有这些都设计到我的小
//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);
显然,上面提到的是网络延迟问题,因此必须对其进行线程化处理。这引入了一个线程抽象,使我们脱离了线程单元测试的实践。我们现在有
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);
})
请原谅我在没有测试的情况下进行反向工作,但我正要带我女儿出去,我正在快速地完成这个例子。这里的想法是,我在一个简单的界面后面协调多个协作者的协作,该界面将绑定到UI按钮单击事件。因此,outter most测试反映了一个Sprint任务,即给定一个“播放电影”按钮,当它被单击时,电影将播放。
编辑
让我们讨论一下。我做了一些相当完整的测试,但它是自动集成测试,而不是单元测试,所以我没有模拟(除了用户:我模拟最终用户,模拟用户输入事件,测试/断言用户的输出):
我想要的是使用TDD的最佳实践。 作为 一种软件开发技术 依赖于一个非常复杂的过程的重复 开发周期短:首先是 开发人员编写了一个失败的自动 定义所需测试的测试用例 改进或新功能,然后 生成代码以通过该测试,并 最后将新代码重构为 可接受的标准 然后,它继续规定:
- 测试与开发是结合在一起的:我在编码后立即进行测试,而不是在编码之前,但无论如何,新代码在签入之前就进行了测试;从来没有未经测试的代码
- 我有自动测试套件,用于回归测试
- 编写测试(相反,我使用UI创建新的测试,它更快、更容易,更接近原始需求)
- 创建模拟(单元测试所需)
- 在重构内部实现时编辑测试(因为测试只依赖于公共API,而不依赖于内部实现细节)
- 我的解决方案(不是CocoaTouch)是继续模拟对象,而是将模拟重构为一种通用的测试方法。这降低了测试本身的复杂性,同时保留了模拟基础结构,以单独测试我的类。有许多模拟对象表明:
1) 你有太多的依赖关系。
重新审视你的代码,并尝试进一步分解它。特别是,尝试将数据转换和处理分开
因为我知道
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');
}
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'));