简体   繁体   中英

Spock Test Failing with MissingPropertyException

I have a simple class that builds a RequestMessage based on a simple rule.

  • If MessageMetadata is found in the database, the message is built using that metadata.
  • Otherwise, the message is built using a service that provides the metadata.

Here is the class along with a Spock test. The test fails with a MissingPropertyException , saying it cannot find the RequestMessage created using the service.

class MessageService{
    MessageBuilder messageBuilder;
    MessageRepository messageRepository;
    MessageMetadaProvider messageMetaDataProvider;

    public RequestMessage getMessage(String id ) {
        try {
            MessageMetadata metadata = repository.findDefaulMessage(id);
            RequestMessage requestMessage = builder.createMessage(metadata);
            return requestMessage;
        } catch (DataNotFoundException e) {
            e.printStackTrace();
        }
        MessageMetadata metadata = messageMetaDataProvider.createNewMessageMetadata(id);
        RequestMessage message = messageBuilder.buildNew(id , metadata);
        return message;
    }
}

def "Build Request Message  "() {
    given:
    String id = '12345'
    MessageBuilder messageBuilder = Mock()
    MessageRepository messageRepository = Mock()
    MessageMetadaProvider messageMetaDataProvider = Mock()
    MessageService service = createMessageService(messageRepository,messageBuilder,messageMetaDataProvider)
    MessageMetadata metadata = new MessageMedata(id, MessageType.DEFAULT)
    when:
    RequestMessage requestMessage = messageService .getMessage(id )
    then:
    1 * messageRepository.repository.findDefaulMessage(id) >> {throw new DataNotFoundException("Not Found")}
    1 * messageMetaDataProvider.createNewMessageMetadata(id) >> metadata
    1  *  messageBuilder.buildNew(id , metadata) >> requestMessage
}

I would really like to understand why this doesn't work. I'm grateful for any help in testing this test successfully. Thank you

The problem is indeed the hen vs. egg problem I mentioned in my comment to jaco0646 's answer. Let me recreate the core problem in an MCVE :

package de.scrum_master.stackoverflow.q60143388;

public class RequestMessage {}
package de.scrum_master.stackoverflow.q60143388;

public class MessageService {
  MessageBuilder messageBuilder;

  public RequestMessage getMessage(String id) {
    return messageBuilder.buildNew(id);
  }
}
package de.scrum_master.stackoverflow.q60143388

import spock.lang.Specification

class MessageServiceTest extends Specification {
  def "build request message"() {
    given:
    def id = "12345"
    MessageBuilder messageBuilder = Mock()
    MessageService messageService = new MessageService()
    messageService.messageBuilder = messageBuilder

    when:
    RequestMessage requestMessage = messageService.getMessage(id)

    then:
    1 * messageBuilder.buildNew(id) >> requestMessage
  }
}

This will yield:

groovy.lang.MissingPropertyException: No such property: requestMessage for class: de.scrum_master.stackoverflow.q60143388.MessageServiceTest

    at de.scrum_master.stackoverflow.q60143388.MessageServiceTest.build request message(MessageServiceTest.groovy:17)

Just declaring requestMessage in the given: block as jaco0646 suggested and then using it as a stub result won't do it alone. You need to make sure that you first declare an object (eg a mock) to be returned by the builder method, but assign the result of the method call under test to another variable so you can compare the two or do whatever verification you like to perform:

package de.scrum_master.stackoverflow.q60143388

import spock.lang.Specification

class MessageServiceTest extends Specification {
  def "build request message"() {
    given:
    def id = "12345"
    RequestMessage requestMessage = Mock()
    MessageBuilder messageBuilder = Mock()
    MessageService messageService = new MessageService()
    messageService.messageBuilder = messageBuilder

    when:
    RequestMessage result = messageService.getMessage(id)

    then:
    1 * messageBuilder.buildNew(id) >> requestMessage
    result.toString().startsWith("Mock for type 'RequestMessage'")
  }
}

Now the test passes and your bootstrapping problem is solved. You cannot create an object with a method call and then specify it as an expected stub result at the same time. It makes no logical sense.

Try declaring requestMessage in the given block rather than the when block.

The Spock lifecycle has some surprising behaviors, one of which is that interactions are processed before the when block.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM