java - 如何测试一个方法及其两个助手之间的交互?

标签 java unit-testing groovy spock

我的 Java 代码结构如下:

public MyClass {
    // some class variables
    ...
    private void process() {
        private MyObject obj; 
        ...
        obj = createHelper();
        ...
        messageHelper(obj, "One of several possible strings");
        ...  
        messageHelper(obj, "Another call with a different string");  
        ...
    }

    private MyObject createHelper {
        MyObject obj = new MyObject();
        // some Setter calls
        ...
        return obj;
    }

    private void messageHelper (MyOject obj, String message) {
        ...
    }

}

我想测试一下,基于属性 obj (我想指定),messageHelper() 接收正确的字符串。换句话说,我需要控制一种方法的结果并有权访问另一种方法的参数。

我对所有这些 Mock/Stub/Spy 的东西仍然很不安。

在我看来,我需要使用“Spy”来MyClassstub CreateHelper()手动”创建的对象,但不确定如何拦截 messageHelper() 的调用参数。

我还注意到Wiki使用 Spies 的注意事项:

Think twice before using this feature. It might be better to change the design of the code under specification.

那么,什么是合适的Spocky方式来完成这项任务呢?

稍微重构的代码: (5/5/14)

public MyClass {
    // some class variables
    private messageSevice = new messageService();
    ...
    private void process() {
        private MyObject obj; 
        ...
        obj = new MyObject(parameters ...);
        ...
        if (someCondition) {
            messageService.produceMessageOne(obj);
        }
        ... 
        if (otherCondition) { 
            messageService.produceMessageTwo(obj);  
        {
        ...
    }

}

public class MessageService implements IMessageService {

    private final static MSG_ONE = "...";
    private final static MSG_TWO = "...";
    ...

    public void produceMessageOne(MyObject obj) {
         produceMessage(obj, MSG_ONE);
         ...
    }
    public void produceMessageOne(MyObject obj) {
         produceMessage(obj, MSG_TWO);
    }

    private void produceMessage(MyObject obj, String message) {
         ...
    }    
}

如果有人建议使用 Spock 进行测试的方式,我将不胜感激。

最佳答案

您提到的警告是正确的。可测试的代码和良好的设计之间存在非常好的相关性(我建议观看 Michael Feathers 的讲座以了解原因 http://www.youtube.com/watch?v=4cVZvoFGJTU )。

使用 spy 往往会引起设计问题,因为它通常是由于无法使用常规模拟和 stub 而引起的。

从您的示例中预测有点困难,因为您显然使用了伪名称,但 MyClass 类的设计似乎违反了单一职责原则( http://en.wikipedia.org/wiki/Single_responsibility_principle ),因为它负责处理、创建和消息传递(3 个职责)。

如果您愿意更改设计,以便处理类 (MyClass) 只进行处理,您将提供另一个进行创建的类 (MyObjectFactory),还有另一个通过构造函数、setter 方法或依赖项注入(inject)执行消息传递的类 (MyObjectMessager)。

使用这种新设计,您可以创建正在测试的类的实例 (MyClass),并向其传递工厂类和消息传递类的模拟对象。然后您就可以在两者上验证您想要的任何内容。

看一下这个示例(使用 Mockito):

public class MyClassTest {
    @Test
    public void testThatProcessingMessagesCorrectly() {
        MyObject object = mock(MyObject.class);
        MyObjectFactory factory = mock(MyObjectFactory.class);
        when(factory.createMyObject()).thenReturn(object);
        MyObjectMessager messager = mock(MyObjectMessager.class);

        MyClass processor = new MyClass(factory, messager);
        processor.process();

        verify(factory).createMyObject();
        verify(messager).message(EXPECTED_MESSAGE_1);
        verify(messager).message(EXPECTED_MESSAGE_2);
        ...
        verify(messager).message(EXPECTED_MESSAGE_N);
    }

    ...
}

这是一个 Spock 示例(未经测试,使用前请仔细检查...):

public class MyClassSpec extends Specification {
    def "check that the right messages are produced with the expected object"() {
        given:
        def messageService = Mock(IMessageService)
        def testedInstance = new MyClass()

        testedInstance.setMessageService(messageService)

        when:
        testedInstance.process()

        then:
        1 * messageService.produceMessageOne(_)
        1 * messageService.produceMessageTwo(_)
    }
}

关于java - 如何测试一个方法及其两个助手之间的交互?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23436130/

相关文章:

java - 为什么 java 中 date.gettime() 总是返回上一个日期,而不是今天的日期?

node.js - vuejs组件单元测试中定义的 'this'变量如何获取

date - Groovy 在 00 :00:00 分钟获取今天的日期

java - quartz.properties 位置在 JBoss AS 7.5 中自动加载

Spring MVC 项目的基于 Java 的配置给出 "The requested resource is not available."

javascript - Sinon.JS 的 stub.callsArg(index) 是做什么的?

xcode - 应用测试 VS 逻辑测试

grails - 这个运算符叫什么,它用于 <=>

xml - 从Gradle中的FileTree运行Junit测试

java - 我们可以在 servlet-filter 的 url-mapping 中没有条件吗