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

我是一个重要的倡导者,正确的测试驱动设计或行为驱动设计,我喜欢写作测试.然而,我将自己编码到一个角落,我需要在一个特定的测试用例中使用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

希望这可以帮助.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


迭代器模式(Iterator)迭代器模式(Iterator)[Cursor]意图:提供一种方法顺序访问一个聚合对象中的每个元素,而又不想暴露该对象的内部表示。应用:STL标准库迭代器实现、Java集合类型迭代器等模式结构:心得:迭代器模式的目的是在不获知集合对象内部细节的同时能对集合元素进行遍历操作
高性能IO模型浅析服务器端编程经常需要构造高性能的IO模型,常见的IO模型有四种:(1)同步阻塞IO(BlockingIO):即传统的IO模型。(2)同步非阻塞IO(Non-blockingIO):默认创建的socket都是阻塞的,非阻塞IO要求socket被设置为NONBLOCK。注意这里所说的N
策略模式(Strategy)策略模式(Strategy)[Policy]意图:定义一系列算法,把他们封装起来,并且使他们可以相互替换,使算法可以独立于使用它的客户而变化。应用:排序的比较方法、封装针对类的不同的算法、消除条件判断、寄存器分配算法等。模式结构:心得:对对象(Context)的处理操作可
访问者模式(Visitor)访问者模式(Visitor)意图:表示一个作用于某对象结构中的各元素的操作,它使你在不改变各元素的类的前提下定义作用于这些元素的新操作。应用:作用于编译器语法树的语义分析算法。模式结构:心得:访问者模式是要解决对对象添加新的操作和功能时候,如何尽可能不修改对象的类的一种方
命令模式(Command)命令模式(Command)[Action/Transaction]意图:将一个请求封装为一个对象,从而可用不同的请求对客户参数化。对请求排队或记录请求日志,以及支持可撤消的操作。应用:用户操作日志、撤销恢复操作。模式结构:心得:命令对象的抽象接口(Command)提供的两个
生成器模式(Builder)生成器模式(Builder)意图:将一个对象的构建和它的表示分离,使得同样的构建过程可以创建不同的表示。 应用:编译器词法分析器指导生成抽象语法树、构造迷宫等。模式结构:心得:和工厂模式不同的是,Builder模式需要详细的指导产品的生产。指导者(Director)使用C
设计模式学习心得《设计模式:可复用面向对象软件的基础》一书以更贴近读者思维的角度描述了GOF的23个设计模式。按照书中介绍的每个设计模式的内容,结合网上搜集的资料,我将对设计模式的学习心得总结出来。网络上关于设计模式的资料和文章汗牛充栋,有些文章对设计模式介绍生动形象。但是我相信“一千个读者,一千个
工厂方法模式(Factory Method)工厂方法模式(Factory Method)[Virtual Constructor]意图:定义一个用于创建对象的接口,让子类决定实例化哪一个类,使一个类的实力化延迟到子类。应用:多文档应用管理不同类型的文档。模式结构:心得:面对同一继承体系(Produc
单例模式(Singleton)单例模式(Singleton)意图:保证一个类只有一个实例,并提供一个访问它的全局访问点。应用:Session或者控件的唯一示例等。模式结构:心得:单例模式应该是设计模式中最简单的结构了,它的目的很简单,就是保证自身的实例只有一份。实现这种目的的方式有很多,在Java中
装饰者模式(Decorator)装饰者模式(Decorator)[Wrapper]意图:动态的给一个对象添加一些额外的职责,就增加功能来说,比生成子类更为灵活。应用:给GUI组件添加功能等。模式结构:心得:装饰器(Decorator)和被装饰的对象(ConcreteComponent)拥有统一的接口
抽象工厂模式(Abstract Factory)抽象工厂模式(Abstract Factory)[Kit]意图:提供一个创建一系列相关或相互依赖对象的接口,而无须指定他们具体的类。应用:用户界面工具包。模式结构:心得:工厂方法把生产产品的方式封装起来了,但是一个工厂只能生产一类对象,当一个工厂需要生
桥接模式(Bridge)桥接模式(Bridge)[Handle/Body]意图:将抽象部分与它的实现部分分离,使他们都可以独立的变化。应用:不同系统平台的Windows界面。模式结构:心得:用户所见类体系结构(Window派生)提供了一系列用户的高层操作的接口,但是这些接口的实现是基于具体的底层实现
适配器模式(Adapter)适配器模式(Adapter)[Wrapper]意图:将类的一个接口转换成用户希望的另一个接口,使得原本由于接口不兼容而不能一起工作的类可以一起工作。应用:将图形类接口适配到用户界面组件类中。模式结构:心得:适配器模式一般应用在具有相似接口可复用的条件下。目标接口(Targ
组合模式(Composition)组合模式(Composition)意图:将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。应用:组合图形、文件目录、GUI容器等。模式结构:心得: 用户(Client)通过抽象类(Component)提供的公用接口统一
原型模式(Prototype)原型模式(Prototype)意图:用原型实例制定创建对象的种类,并且通过拷贝这些原型创建新的对象。应用:Java/C#中的Clonable和IClonable接口等。模式结构:心得:原型模式本质上就是对象的拷贝,使用对象拷贝代替对象创建的原因有很多。比如对象的初始化构
什么是设计模式一套被反复使用、多数人知晓的、经过分类编目的、代码 设计经验 的总结;使用设计模式是为了 可重用 代码、让代码 更容易 被他人理解、保证代码 可靠性;设计模式使代码编制  真正工程化;设计模式使软件工程的 基石脉络, 如同大厦的结构一样;并不直接用来完成代码的编写,而是 描述 在各种不同情况下,要怎么解决问题的一种方案;能使不稳定依赖于相对稳定、具体依赖于相对抽象,避免引
单一职责原则定义(Single Responsibility Principle,SRP)一个对象应该只包含 单一的职责,并且该职责被完整地封装在一个类中。Every  Object should have  a single responsibility, and that responsibility should be entirely encapsulated by t
动态代理和CGLib代理分不清吗,看看这篇文章,写的非常好,强烈推荐。原文截图*************************************************************************************************************************原文文本************
适配器模式将一个类的接口转换成客户期望的另一个接口,使得原本接口不兼容的类可以相互合作。
策略模式定义了一系列算法族,并封装在类中,它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。