Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spock How to mock Autowired class' function call within a method

I have a class that I want to test in that looks like this: package com.something;

import org.springframework.beans.factory.annotation.Autowired;
public class ClassToTest implements InterfaceToTest{

  @Autowired
  AnotherService serviceA;

  @Override
  public List<String> methodToTest(List<String> randomVar){
    ...
    String stringA = serviceA.someFunction(randomVar);
    ...
  }
}

How can I mock the results from the call to serviceA.someFunction(randomVar) to return any String of my choice when testing with spock?

package com.something;
import spock.lang.Shared
import spock.lang.Specification
class TestClass extends Specification{
  @Shared InterfaceToTest classToTest = new ClassToTest()

  static doWithSpring = {
    serviceA(AnotherService)
  }

  def "tests-part-1"(){
    when: "something"
    ...
    then: "expect this"
    ...
  }
}

I dont know where to go from here. My IDE shows errors with the doWithSpring code I added to the testing class. Any ideas on how to deal with this?

like image 544
Cyriac Domini Thundathil Avatar asked Aug 10 '16 15:08

Cyriac Domini Thundathil


2 Answers

I would suggest thinking of it from more of a unit testing perspective. You want to mock out the spring framework stuff and just make sure that you're testing your logic. This is very easy to do with Spock.

ClassToTest myClass = new ClassToTest(serviceA: Mock(AnotherService))

def "test my method"() {
  when:
  myClass.methodToTest([])

  then:
  1 * myClass.serviceA.someFunction([]) >> 'A string'
}

From here, you can look at data driving it or using a >>> and passing a list of the different strings that you'd like to return.

like image 116
rockympls Avatar answered Nov 15 '22 06:11

rockympls


A simple solution to enable unit testing would be to alter ClassToTest to have a constructor which sets the serviceA field like this:

import org.springframework.beans.factory.annotation.Autowired;
public class ClassToTest implements InterfaceToTest{

private AnotherService serviceA;

@Autowired
public ClassToTest(final AnotherService serviceA){
   this.serviceA = serviceA;
}

@Override
public List<String> methodToTest(List<String> randomVar){
...
String stringA = serviceA.someFunction(randomVar);
...
}
}

Then in your spock unit test you can provide a mock in the constructor:

class TestClass extends Specification{
 def mockServiceA = Mock(AnotherService)
 @Shared InterfaceToTest classToTest = new ClassToTest(mockServiceA)

And in each test case you can mock in the usual spock way:

1 * mockServiceA.someFunction(_) >> 'A string'
like image 43
conorstwo Avatar answered Nov 15 '22 06:11

conorstwo