web-dev-qa-db-ja.com

Grails / Spock:クラス自体からメソッドが呼び出されるクラス内の単一のメソッドをモックする方法は?

次の場合、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) {

    }
}
12
John

spies と部分モックを使用できます(Spock 0.7以降が必要です)。

スパイを作成した後、発信者とスパイの基礎となる実際のオブジェクトとの間の会話を聞くことができます。

def subscriber = Spy(SubscriberImpl, constructorArgs: ["Fred"])
subscriber.receive(_) >> "ok"

コードを実行し、実際のメソッドに委任することが望ましい場合があります。

subscriber.receive(_) >> { String message -> callRealMethod(); message.size() > 3 ? "ok" : "fail" }
7
Gregor Petrin

私の意見では、これはうまく設計されたソリューションではありません。テストと設計は手をつないで歩きます-私はお勧めします this それをよりよく調査するために話してください。テスト中のオブジェクトで他のメソッドが呼び出されたかどうかを確認する必要がある場合は、別の責任を持つ他のオブジェクトに移動する必要があるようです。

これが私がそれをする方法です。 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) }
   }
}
2
Opal

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()
    }
}
1
juanjocv

For Java Springの人々がSpockでテストしています:

constructorArgsが進むべき道ですが、コンストラクター注入を使用してください。 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)
    }
}

// **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"
    }
}      

Spock-Spyオブジェクトのvoidメソッドをスタブします

// **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:
            ...
    }
} 
1
Dmitry