简体   繁体   中英

Spock + Spring - Stubs returned from Stubbed @SpringBean always return null

I am attempting to use Spock to create an integration test around a Spring application. As it is not a Spring Boot application and the @SpringBootTest annotation interfered significantly with the app's initialization, I am using a minimal configuration.

I specifically need to stub a service in my app that returns objects of type Message ; in the actual app these objects would come from a third-party vendor's library and they cannot be instantiated or subclassed, nor do their members have setters, so my only option is to create Stub s for them. However, with this current configuration (I've simplified the test significantly just to get the gist across):

@ContextConfiguration([TestSetup]) // supplies actual Spring beans including some JPA repos
class LogicSpec extends Specification {
  @SpringBean
  RestService restService = Stub()
  @Autowired
  ServiceUnderTest sut
  @Autowired
  SomeJPARepo repository;

  def 'should do some business logic'() {
    given:
      Message m = new Stub() {
        getStatus() >> "stubbed status"
      }
      restService.getMessage(_ as String) >> {
        m
      }
    when:
      sut.businessMethod()
    then:
      // just checking for side effects that `businessMethod` causes, no mocks being matched against
      assert repository.findAll().every { it.processed == true }  
    }
  }
}

Internally, the ServiceUnderTest.businessMethod() is using the Message object like this:

restService.getMessage(sid).getStatus() // should be "stubbed status"; always evaluates to null

however, every method of the Message stub always returns null regardless of whether I have defined a behavior for it. The Message objects must return specific values from its getters for the test to work. I would prefer to not have to declare every Message stub as its own @SpringBean ; I need to eventually expand the test to use several different Message stub objects. I don't need mocks or spies because the number of invocations of RestService 's methods doesn't matter, I just need it to emit proper stubs for ServiceUnderTest to chew on. Apologies if this question is unusual or I've missed something obvious; I'm slightly oblivious to Spock's notion of lifecycle, and the waters have been especially muddied with the addition of the Spring extension.

I discovered the answer soon after writing this, but just for posterity; the third-party Message class is declared as final and thus can't be subclassed; Spock was creating stubs for them but silently failing to add the overridden mock methods. I ended up using PowerMockito to remove this limitation; however this interfered with collecting test coverage metrics, so I instead used a wrapper class that can be mocked and used it everywhere in my code the original Message class was:

public class MessageWrapper {
  public MessageWrapper(Message from) {...}
}

it's an extra bit of headache, but it was necessary because test coverage was required in this case. There also seems to be a promising Spock-specific mocking utility that will mock final classes, but I haven't tested it nor do I know if it will interfere with collecting coverage metrics like PowerMockito does.

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