Unit testing Grails/Spock:如何在类中模拟单个方法,其中方法是从类本身中调用的?

Unit testing Grails/Spock:如何在类中模拟单个方法,其中方法是从类本身中调用的?,unit-testing,grails,groovy,spock,Unit Testing,Grails,Groovy,Spock,鉴于以下情况,如何使用Spock模拟processMessage(),以便检查processBulkMessage()调用processMessage()n次,其中n是BulkMessage中的消息数 class BulkMessage { List messages } class MyService { def processBulkMessage(BulkMessage msg) { msg.messages.each {subMsg->

鉴于以下情况,如何使用Spock模拟processMessage(),以便检查processBulkMessage()调用processMessage()n次,其中n是BulkMessage中的消息数

class BulkMessage {
    List messages
}

class MyService {

    def processBulkMessage(BulkMessage msg) {
        msg.messages.each {subMsg->
            processMessage(subMsg)
        }
    }

    def processMessage(Message message) {

    }
}

它不使用Spock内置的模拟API(不确定如何部分模拟对象),但这应该可以做到:

class FooSpec extends Specification {

    void "Test message processing"() {
        given: "A Bulk Message"
        BulkMessage bulk = new BulkMessage(messages: ['a', 'b', 'c'])

        when: "Service is called"
        def processMessageCount = 0
        MyService.metaClass.processMessage { message -> processMessageCount++ }
        def service = new MyService()
        service.processBulkMessage(bulk)

        then: "Each message is processed separately"
        processMessageCount == bulk.messages.size()
    }
}
您可以使用和部分模拟(需要Spock 0.7或更新版本)

创建间谍后,您可以监听调用者与间谍背后的真实对象之间的对话:

有时,需要执行一些代码并委托给实际方法:


在我看来,这不是一个精心设计的解决方案。测试和设计手拉手——我建议通过谈话更好地进行研究。如果需要检查是否对正在测试的对象调用了其他方法,那么似乎应该将其移动到具有不同职责的其他对象

我会这样做的。我知道在groovy中可见性是如何工作的,所以请注意下面的评论

@Grab('org.spockframework:spock-core:0.7-groovy-2.0')
@Grab('cglib:cglib-nodep:3.1')

import spock.lang.*

class MessageServiceSpec extends Specification {

    def 'test'() {
        given:
        def service = new MessageService()
        def sender = GroovyMock(MessageSender)

        and:
        service.sender = sender

        when:
        service.sendMessages(['1','2','3'])

        then:
        3 * sender.sendMessage(_)
    }
}
class MessageSender { //package access - low level   
   def sendMessage(String message) {
      //whatever
   }
}

class MessageService {

   MessageSender sender //package access - low level

   def sendMessages(Iterable<String> messages) {
      messages.each { m -> sender.sendMessage(m) }
   }
}
@Grab('org.spockframework:spock-core:0.7-groovy-2.0')
@抓取('cglib:cglib nodep:3.1')
导入spock.lang*
类MessageServiceSpec扩展了规范{
def“测试”(){
鉴于:
def service=newmessageservice()
def sender=GroovyMock(MessageSender)
以及:
service.sender=发送方
什么时候:
service.sendMessages(['1'、'2'、'3']))
然后:
3*发送者。发送消息(\ux)
}
}
类MessageSender{//包访问-低级别
def sendMessage(字符串消息){
//随便
}
}
类消息服务{
MessageSender//包访问-低级别
def发送消息(可编辑消息){
messages.each{m->sender.sendMessage(m)}
}
}

用于在Spock中进行Java Spring测试:

构造函数args是一种方法,但使用构造函数注入。Spy()不允许您直接设置自动连接字段

// **Java Spring**
class A {
    private ARepository aRepository;

    @Autowire
    public A(aRepository aRepository){
        this.aRepository = aRepository;
    }

    public String getOne(String id) {
        tryStubMe(id)  // STUBBED. WILL RETURN "XXX"
        ...
    }

    public String tryStubMe(String id) {
        return aRepository.findOne(id)
    }

    public void tryStubVoid(String id) {
        aRepository.findOne(id)
    }
}

Spock-Spy对象上的存根无效方法

// **Groovy Spock**
class ATest extends Specification {

    def 'lets stub that sucker' {
        setup:
            ARepository aRepository = Mock()
            A a = Spy(A, constructorArgs: [aRepository]) {
                1 * tryStubVoid(_) >> {}
            }
        when:
            ...
        then:
            ...
    }
} 

非常感谢-我刚刚重新讨论了这个问题,自从我提出这个问题以来,我学到了很多关于设计的知识。我现在同意你的观点,更好的设计应该是一个bulkMessageProcessingService和一个individualMessageProcessingService。因此,使用mock进行测试非常简单。
// **Java Spring**
class A {
    private ARepository aRepository;

    @Autowire
    public A(aRepository aRepository){
        this.aRepository = aRepository;
    }

    public String getOne(String id) {
        tryStubMe(id)  // STUBBED. WILL RETURN "XXX"
        ...
    }

    public String tryStubMe(String id) {
        return aRepository.findOne(id)
    }

    public void tryStubVoid(String id) {
        aRepository.findOne(id)
    }
}
// **Groovy Spock**
class ATest extends Specification {

    def 'lets stub that sucker' {
        setup:
            ARepository aRepository = Mock()
            A a = Spy(A, constructorArgs: [aRepository])
        when:
            a.getOne()
        then:
            // Stub tryStubMe() on a spy
            // Make it return "XXX"
            // Verify it was called once
            1 * a.tryStubMe("1") >> "XXX"
    }
}      
// **Groovy Spock**
class ATest extends Specification {

    def 'lets stub that sucker' {
        setup:
            ARepository aRepository = Mock()
            A a = Spy(A, constructorArgs: [aRepository]) {
                1 * tryStubVoid(_) >> {}
            }
        when:
            ...
        then:
            ...
    }
}