Hot questions for Using Mockito in spock

Hot questions for Using Mockito in spock

Question:

I have a setup of a simple controller:

@Controller
@RequestMapping("/test")
public class TestController {

    @RequestMapping(value = "/{id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
    public @ResponseBody List<String> getTestString(){
        List<String> sampleTest = new ArrayList<String>();
        sampleTest.add("Test");

        return sampleTest;
    }
}

For this simple controller I'm trying to write a test in Spock using MockMVC:

class TestControllerTest extends Specification {

    MockMvc mockMvc;

    def setup(){
        mockMvc = MockMvcBuilders.standaloneSetup(new TestController()).build();
    }

    def "testing TestController"(){
        when:
        MvcResult response = mockMvc.perform(get("/test/1"));

        then:
        response.andExpect(content().string('["Test"]'));
    }
}

The JARs I have are:

Spring-test:4.0.5 Javax-servlet-api:3.0.1 spock-spring:0.7-groovy-2.0

The Error I get after running the test is this:

groovy.lang.MissingMethodException: No signature of method: com.crmservice.controller.TestControllerTest.get() is applicable for argument types: (java.lang.String) values: [/test]
Possible solutions: getAt(java.lang.String), grep(), grep(java.lang.Object), wait(), Spy(), any()
    at com.crmservice.controller.TestControllerTest.testing TestController(TestControllerTest.groovy:27)

Answer:

Is the missing get method imported?

You need the following line in imports block:

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get

Question:

I have some tests written in Spock which covers my Java code. Now I migrate to Kotlin and the problem is i cannot mock final classes so i decide to use Mockito plugin described here: https://github.com/mockito/mockito/wiki/What%27s-new-in-Mockito-2#unmockable

The question is do I need to replace every '_' '>>' with Mockitos' any(), anyString(), when(), then() etc? I tried to just mock final classes using Mockito but it seems not work.

If i have to replace what the advantage is for using Spock testing framework in this case? Maybe i should remove it and stay with Mockito only?


Answer:

I do not use Kotlin, but I have used PowerMock before for mocking final classes/methods and static methods. I was wondering about whether it might be possible to use Spock's GroovyMock and global GroovySpy features and tested it against Java code using Spock 1.1-groovy-2.4. In a first quick & dirty test scenario it seems to work:

Java class under test:

package de.scrum_master.stackoverflow;

public final class FinalClass {
  public static final String finalStaticMethod() {
    return "x";
  }

  public final String finalMethod() {
    return "x";
  }
}

Spock test:

package de.scrum_master.stackoverflow

import spock.lang.Specification

/**
 * See https://stackoverflow.com/q/48391716/1082681
 * See http://spockframework.org/spock/docs/1.1/all_in_one.html#GroovyMocks
 */
class FinalClassTest extends Specification {
  def "use GroovyMock for final method in final class"() {
    given:
    FinalClass finalClass = GroovyMock() {
      finalMethod() >> "mocked"
    }

    expect:
    finalClass.finalMethod() == "mocked"
  }

  def "use global GroovySpy for final static method in final class"() {
    given:
    GroovySpy(FinalClass, global: true)
    FinalClass.finalStaticMethod() >> "mocked"

    expect:
    FinalClass.finalStaticMethod() == "mocked"
  }
}

For me both feature methods were green when running the test. Maybe you want to give it a try with my example and subsequently with your Kotlin classes - no guarantees for the latter from me, though.


Attention: The Spock manual says:

When called from Java code, Groovy mocks will behave like regular mocks.

So maybe you will get disappointed when injecting Groovy Mocks as dependencies into Java classes under test.


Update: Okay, I tested it with another Java class using those fancy GroovyMocks and - as mentioned above - it does not work:

Java class using the mocked class as a dependency:

package de.scrum_master.stackoverflow;

public class AnotherClass {
  public String doSomething(FinalClass finalClass) {
    return finalClass.finalMethod();
  }

  public String doSomethingElse() {
    return FinalClass.finalStaticMethod();
  }
}

Spock test:

package de.scrum_master.stackoverflow

import spock.lang.Specification

/**
 * See https://stackoverflow.com/q/48391716/1082681
 * See http://spockframework.org/spock/docs/1.1/all_in_one.html#GroovyMocks
 */
class AnotherClassTest extends Specification {
  def "indirectly use GroovyMock for final method in final class"() {
    given:
    FinalClass finalClass = GroovyMock() {
      finalMethod() >> "mocked"
    }

    expect:
    new AnotherClass().doSomething(finalClass) == "mocked"
  }

  def "indirectly use global GroovySpy for final static method in final class"() {
    given:
    GroovySpy(FinalClass, global: true)
    FinalClass.finalStaticMethod() >> "mocked"

    expect:
    new AnotherClass().doSomethingElse() == "mocked"
  }
}

Unfortunately, both tests fail because the methods do not get stubbed when used from the Java class. I.e. you are stuck with PowerMock or Mockito. But still you can use all the other nice Spock features such as data tables, @Unroll and many more.


Update 2: THE SOLUTION

Add this to your Maven build (if you use Gradle, do something similar):

<dependency>
  <groupId>de.jodamob.kotlin</groupId>
  <artifactId>kotlin-runner-spock</artifactId>
  <version>0.3.1</version>
  <scope>test</scope>
</dependency>

Now you can use the SpotlinTestRunner from project kotlin-testrunner in combination with annotations like

  • @OpenedClasses([Foo, Bar, Zot])
  • @OpenedPackages(["de.scrum_master.stackoverflow", "my.other.package"])

Of course this will not work for static methods (you still need PowerMock for it), but your question was about non-static methods in closed Kotlin classes. With this test runner you can just mock them because a special classloader opens them up via Javassist before test execution:

package de.scrum_master.stackoverflow

import de.jodamob.kotlin.testrunner.OpenedClasses
import de.jodamob.kotlin.testrunner.OpenedPackages
import de.jodamob.kotlin.testrunner.SpotlinTestRunner
import org.junit.runner.RunWith
import spock.lang.Specification

/**
 * See https://stackoverflow.com/q/48391716/1082681
 * See https://github.com/dpreussler/kotlin-testrunner
 */
@RunWith(SpotlinTestRunner)
@OpenedClasses(FinalClass)
//@OpenedPackages("de.scrum_master.stackoverflow")
class AnotherClassSpotlinRunnerTest extends Specification {
  def "use SpotlinRunner to stub final method in final class"() {
    given:
    FinalClass finalClass = Stub() {
      finalMethod() >> "mocked"
    }

    expect:
    new AnotherClass().doSomething(finalClass) == "mocked"
  }
}

Question:

How to do the equivalent of Mockito's deep mock / stub (RETURNS_DEEP_STUBS ) in Spock? Something like:

Changes changes = Mock()
changes.id(_).current() >> aChangeApi

While in Mockito it'd be:

Changes changes = mock(Changes.class, RETURNS_DEEP_STUBS);
when(changes.id(any()).current()).thenReturn(aChangeApi);

Answer:

I think you can do something like:

Changes changes = Stub()
changes.id(_) >> Stub(<ReturnedClass>) {
    changes() >> aChangeApi
}

This just returns a stub which can then be further mocked. I'm not that familiar with Mockito but from a bit of google searching this seems to be the way that should get a similar result.

Question:

I'm trying to spy a Java class in a Spock test. I've not had a problem with Mockito in Spock/Groovy before.

When I try to do the following:

def service = spy(Service.class)

I get the following error:

org.mockito.exceptions.base.MockitoException: 
Cannot mock/spy class java.lang.Class
Mockito cannot mock/spy following:
  - final classes
  - anonymous classes
  - primitive types

When I do mock(Service.class) though, it works fine.

I have confirmed that the class is not final, anonymous, or primitive.

Any ideas? I have a random generator in the class (ak) so I need to spy not mock.

Thank you


Answer:

Mockito doesn't spy on Classes (or Mocks), it spies on (regular) Objects. Thus, instead of

def service = spy(Service.class)

you have to write

def service = spy(new Service())

(or whichever constructor is appropriate for your scenario).

Question:

How to mock the private method and method in a different class in the test class?

class MyClass {
    private final Retriever<ScoreData> retriever;
    private DataStore<Model> dataStore;
    private String gameName;

    public void MyClass(Retriever<ScoreData> retriever, DataStore<Model> dataStore, String gameName) {
        this.retriever = retriever;
        this.dataStore = dataStore;
        this.gameName = gameName;
    }

    public void process(GameHolder<G> games) {
        // Business Logic
        for (Game<G> game : games){
        Integer score = game.getScore();
        Integer playerId = game.getPlayerId();
        Integer finalScore = getScore(game);
        computeScore(score, finalScore);
        }
    }

    private Integer computeScore(int score, int finalScore) {
        // Runs some business logic and returns O3
        return score + finalScore;
    }

    private Integer getScore(Game game) {
        // Runs some business logic and returns O3
        String dbName = game.getDbName();
        DBRetriever ret = new DBRetriever(dbName)
        if (dbName.equals("gameDB"){
            return ret.getFinalScore(dbName);
        }
        return -1;
    }

}

Below is my current implementation for Spock and I am not sure how to implement the mocking for objects.

@Subject
def obj

def "this is my test"(){
    given:
    Object1 obj1 = Mock(Object1)
    Object2 obj2 = Mock(Object2)
    Object3 obj3 = Mock(Object3)

    def myClassObject = new MyClass(obj1, obj2, obj3)

    when:
    myClassObject.process(new Object4())

    then:
    1 * getScore()
    1 * computeScore()

}

How can I mock the computeScore and getScore functions and how can I assign the initial values for objects obj1, obj2, obj3?

Note: I am only trying to test process() method here. But process method is calling a private method from inside. I want to be able to return a mock value for private method rather than executing the private method.

Edit: Retriever and DataStore are interfaces and their respective implementations are ScoreData and Model.


Answer:

Note: I am only trying to test process() method here. But process method is calling a private method from inside. I want to be able to return a mock value for private method rather than executing the private method.

You should not do that because MyClass is your class under test. You cannot cover the logic inside the private methods with tests if you stub them. Instead, you should make sure that the injected mocks behave the way you want them to (via stubbed methods) if they are used inside those private method. Unfortunately you decided not to show that crucial part of your code even though the exact answer depends on it. Instead you replaced them with comments "some business logic", which is not very helpful because your business logic is what you want to test. You don't want to stub it out.


So please don't do what I am showing you here, I am answering only because you asked.

In order to stub a method it must not be private because spies, mocks or stubs technically are always subclasses or the originals and subclasses cannot inherit or even call private methods. Thus, you need to make the methods protected (so subclasses can use or override them) or package-scoped. I recommend the former.

But you cannot use a normal mock or stub as a stand-in for your class under test because you only want to stub out part of the business logic (your two methods in question), not the whole logic (you want to keep process()). Thus, you need a partial mock. For this purpose you can use a spy.

Dummy dependency classes:

package de.scrum_master.stackoverflow.q60103582;

public class Object1 {}
package de.scrum_master.stackoverflow.q60103582;

public class Object2 {}
package de.scrum_master.stackoverflow.q60103582;

public class Object3 {}
package de.scrum_master.stackoverflow.q60103582;

public class Object4 {}

Class under test:

package de.scrum_master.stackoverflow.q60103582;

public class MyClass {
  private Object1 o1;
  private Object2 o2;
  private Object3 o3;

  public MyClass(Object1 o1, Object2 o2, Object3 o3) {
    this.o1 = o1;
    this.o2 = o2;
    this.o3 = o3;
  }

  public void process(Object4 o4) {
    System.out.println("process - business Logic");
    Object2 result = getScore("dummy ID");
    Object3 obj = computeScore(result);
  }

  protected Object3 computeScore(Object2 result) {
    System.out.println("computeScore - business logic");
    return o3;
  }

  protected Object2 getScore(String id) {
    System.out.println("getScore - business logic");
    return o2;
  }
}

Spock test:

package de.scrum_master.stackoverflow.q60103582

import spock.lang.Specification

class MyClassTest extends Specification {
  def "check main business logic"(){
    given:
    Object1 obj1 = Mock()
    Object2 obj2 = Mock()
    Object3 obj3 = Mock()

    MyClass myClass = Spy(constructorArgs: [obj1, obj2, obj3])

    when:
    myClass.process(new Object4())

    then:
    1 * myClass.getScore(_) //>> obj2
    1 * myClass.computeScore(_) //>> obj3
  }
}

Here you can see how to check interactions on the spy. But note that computeScore(_) and getScore(_) will still be executed, as you can see in the console log:

process - business Logic
getScore - business logic
computeScore - business logic

If you uncomment the end of the last two lines of code

    1 * myClass.getScore(_) >> obj2
    1 * myClass.computeScore(_) >> obj3

you will actually avoid the two (protected) methods from being executed altogether and replace them by stub results. The console log will change to:

process - business Logic

But I am saying it again: Don't do this. Instead make sure your injected mocks show the right behaviour so you can actually execute the methods in your class under test. This is what testing is about, isn't it?

Question:

I'm writing my unit tests using the Spock and Mockito frameworks and have stumbled across a limitation in Mockito which I cannot solve elegantly.

The following code parses a .csv file and returns a Collection of TradedInstrument objects:

@ManagedOperation
public Collection<TradedInstrument> loadTradedInstrumentFromBatchFile() {
    Collection<TradedInstrument> tradedInstrumentsFrombatchFile = new ArrayList<>();
    try (BufferedReader br = new BufferedReader(new InputStreamReader(
                this.getClass().getResourceAsStream("/" + tradedInstrumentBatchFilename)))) {
        String line;
        while ((line = br.readLine()) != null) {
            String[] instrumentAttributes = line.split(",");
            TradedInstrument tradedInstrument = new TradedInstrument();
            tradedInstrument.setInstId(instrumentAttributes[0]);
            tradedInstrument.setPriceSource(PriceSource.valueOf(instrumentAttributes[1]));
            tradedInstrument.setPrice(new BigDecimal(instrumentAttributes[2]));
            tradedInstrument.setDateCreated(businessDateDao.getBusinessDate());

            insertTradedInstrument(tradedInstrument);
            tradedInstrumentsFrombatchFile.add(tradedInstrument);
            LOGGER.info("Loaded traded instrument: " + tradedInstrument + " from batch file: " + tradedInstrumentBatchFilename);
        }

    } catch (NullPointerException e) {
        String errorMessage = "An ERROR occurred locating the traded instrument upload batch file: " + tradedInstrumentBatchFilename;
        LOGGER.error(errorMessage);
        throw new PriceServiceException(errorMessage, e);
    } catch (IOException e) {
        String errorMessage = "An ERROR occurred reading the traded instrument upload batch file: " + tradedInstrumentBatchFilename;
        LOGGER.error(errorMessage);
        throw new PriceServiceException(errorMessage, e);
    } catch (Exception e) {
        String errorMessage = "An ERROR occurred creating traded instrument using data from upload batch file: " + tradedInstrumentBatchFilename;
        LOGGER.error(errorMessage, e);
        throw new PriceServiceException(errorMessage, e);
    }
    return tradedInstrumentsFrombatchFile;
}

I have a batch file called "tradedInstrumentBatchFile.csv" with the following content:

1234,ICAP,0.4956 2345,BBG,0.8456 8456,NASDAQ,0.3567 5967,REUTERS,0.8675

I've written the following Spock test in Groovy:

def "should load traded instrument from batch file"() {
    given:
    TradedInstrumentSubscriber tradedInstrumentSubscriber = Mock()
    TradedInstrumentTable tradedInstrumentDao = Mock()
    VendorTopicDao vendorTopicDao = Mock()
    TradedInstrumentListener listener = Mock()
    BusinessDateDao businessDateDao = Mock()

    PriceService priceService = new PriceService(tradedInstrumentSubscriber, listener, vendorTopicDao, tradedInstrumentDao, businessDateDao)
    priceService.setTradedInstrumentBatchFilename("tradedInstrumentBatchFile.csv")
    businessDateDao.getBusinessDate() >> new Date()

    when:
    priceService.loadTradedInstrumentFromBatchFile()

    then:
    1 * tradedInstrumentDao.insert(_ as TradedInstrument) >> { TradedInstrument arg ->
        assert arg.instId == "1234"
        assert arg.priceSource == PriceSource.ICAP
        assert arg.price == BigDecimal.valueOf(0.4956)
    }

    1 * tradedInstrumentDao.insert(_ as TradedInstrument) >> { TradedInstrument arg ->
        assert arg.instId == "2345"
        assert arg.priceSource == PriceSource.BBG
        assert arg.price == BigDecimal.valueOf(0.8456)
    }

    1 * tradedInstrumentDao.insert(_ as TradedInstrument) >> { TradedInstrument arg ->
        assert arg.instId == "8456"
        assert arg.priceSource == PriceSource.NASDAQ
        assert arg.price == BigDecimal.valueOf(0.3567)
    }

    1 * tradedInstrumentDao.insert(_ as TradedInstrument) >> { TradedInstrument arg ->
        assert arg.instId == "5967"
        assert arg.priceSource == PriceSource.REUTERS
        assert arg.price == BigDecimal.valueOf(0.8675)
    }
}

I've written an attempt at the equivalent Junit test using Mockito:

 @Test
public void shouldLoadTradedInstrumentFromBatchFile() {
    // Given
    VendorTopicDao vendorTopicDao = mock(VendorTopicDao.class);
    TradedInstrumentSubscriber tradedInstrumentSubscriber = mock(TradedInstrumentSubscriber.class);
    TradedInstrumentTable tradedInstrumentDao = mock(TradedInstrumentTable.class);
    TradedInstrumentListener listener = mock(TradedInstrumentListener.class);
    BusinessDateDao businessDateDao = mock(BusinessDateDao.class);
    PriceService priceService = new PriceService(tradedInstrumentSubscriber, listener, vendorTopicDao, tradedInstrumentDao);
    priceService.setTradedInstrumentBatchFilename("tradedInstrumentBatchFile.csv");

    ArgumentCaptor<TradedInstrument> captor = ArgumentCaptor.forClass(TradedInstrument.class);

    when(businessDateDao.getBusinessDate()).thenReturn(new Date());

    // When
    priceService.loadTradedInstrumentFromBatchFile();

    // Then
    verify(tradedInstrumentDao, times(4)).insert(captor.capture());
    List<TradedInstrument> tradedInstruments = captor.getAllValues();

    TradedInstrument tradedInstrument1 = new TradedInstrument();
    tradedInstrument1.setInstId("1234");
    tradedInstrument1.setPriceSource(PriceSource.ICAP);
    tradedInstrument1.setPrice(BigDecimal.valueOf(0.4956));

    TradedInstrument tradedInstrument2 = new TradedInstrument();
    tradedInstrument2.setInstId("2345");
    tradedInstrument2.setPriceSource(PriceSource.BBG);
    tradedInstrument2.setPrice(BigDecimal.valueOf(0.8456));

    TradedInstrument tradedInstrument3= new TradedInstrument();
    tradedInstrument2.setInstId("8456");
    tradedInstrument2.setPriceSource(PriceSource.NASDAQ);
    tradedInstrument2.setPrice(BigDecimal.valueOf(0.3567));

    TradedInstrument tradedInstrument4= new TradedInstrument();
    tradedInstrument2.setInstId("5967");
    tradedInstrument2.setPriceSource(PriceSource.REUTERS);
    tradedInstrument2.setPrice(BigDecimal.valueOf(0.8675));

    assertThat(tradedInstruments, hasItem(tradedInstrument1));
    assertThat(tradedInstruments, hasItem(tradedInstrument2));
    assertThat(tradedInstruments, hasItem(tradedInstrument3));
    assertThat(tradedInstruments, hasItem(tradedInstrument4));
}

The Junit test fails because the test includes the field "dateCreated" in the comparison whereas, the Spock test selectively omits this field.

The field "dateCreated" is supposed to be the actual time the TradedInstrument was created so it intentionally needs to be excluded from the comparison.

The only solution I can see is to add the following interaction on the businessDateDao:

when(businessDateDao.getBusinessDate()).thenReturn(null);

Is there an equivalent way of using a Matcher in Junit/ Mockito to selectively omit the comparison of a field?


Answer:

You could use a combination of hasItem and hasProperty to focus your assertions on the attributes of TradedInstrument which are populated. For example:

assertThat(tradedInstruments, hasItem(hasProperty("instId", is(1234))));
assertThat(tradedInstruments, hasItem(hasProperty("priceSource", is(PriceSource.ICAP))));
assertThat(tradedInstruments, hasItem(hasProperty("price", is(BigDecimal.valueOf(0.4956)))));

Or you could declare a custom matcher ...

private Matcher<TradedInstrument> isEquivalent(final String instId, final PriceSource priceSource, final BigDecimal price) {
    return new BaseMatcher<TradedInstrument>() {
        @Override
        public boolean matches(final Object item) {
            final TradedInstrument tradedInstrument = (TradedInstrument) item;
            // your custom equality implementation e.g.
            return instId.equals(tradeInstrument.getInstId()) && priceSource == tradeInstrument.getPriceSource() && price.equals(tradeInstrument.getPrice());
        }
        @Override
        public void describeTo(final Description description) {
            description.appendText(String.format("the given object should contain id=%s, priceSource=%s, price=%s ", id, priceSource, price));
        }
    };
}

... and use it like this:

assertThat(tradedInstruments, hasItem(isEquivalent(1, PriceSource.ICAP, BigDecimal.valueOf(0.4956))));    

Question:

I have an implementation of Spring interface UserDetailsService:

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    private final UserRepository userRepository;

    @Autowired
    public UserDetailsServiceImpl(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        final UserEntity user = userRepository.findByUsername(username);

        if (user == null) {
            throw new UsernameNotFoundException("Cannot find user with username " + username);
        }

        return new User(user);
    }
}

UserRepository is a standard interface extendng JpaRepository<UserEntity, Long> where UserEntity is my model class.

User is an implementation of UserDetails from Spring Framework.

And I wrote an unit test for this method using JUnit and Mockito. These tests are working:

@RunWith(SpringRunner.class)
@DirtiesContext
public class UserDetailsServiceTest {

    @Autowired
    private UserDetailsService userDetailsService;

    @Test
    public void shouldFindUser() throws Exception {
        // given
        final UserEntity user = new UserEntity(
                1L,
                "username",
                "username@email.com",
                "password",
                new ArrayList<>() // list of roles
        );

        when(UserDetailsServiceTestContext.userRepository.findByUsername(user.getUsername()))
                .thenReturn(user);

        // when
        final UserDetails result = userDetailsService.loadUserByUsername(user.getUsername());

        // then
        assertThat(result).isEqualTo(UserFactory.create(user));
        verify(UserDetailsServiceTestContext.userRepository)
                .findByUsername(user.getUsername());
    }

    @Test(expected = UsernameNotFoundException.class)
    public void shouldNotFindUser() throws Exception {
        // given
        when(UserDetailsServiceTestContext.userRepository.findByUsername(anyString()))
                .thenReturn(null);

        // when
        final UserDetails result = userDetailsService.loadUserByUsername(new String());
    }

    @TestConfiguration
    static class UserDetailsServiceTestContext {

        @MockBean
        private static UserRepository userRepository;

        @Bean
        UserDetailsService userDetailsService() {
            return new UserDetailsServiceImpl(userRepository);
        }
    }

}

And now I try to write these tests using Groovy and Spock framework. I wrote the following specification:

def 'should find user'() {
        given:
            def user = new UserEntity(
                1L,
                "username",
                "username@email.com",
                "password"
                new ArrayList<>() // list of roles
            )

            userRepository.findByUsername(user.username) >> user
            // userRepository.findByUsername(_ as String) >> user // also working

        when:
            def result = userDetailsService.loadUserByUsername(user.username)

        then:
            result == new User(user)
    }

and this test is working. But when I want verify calling of userRepository by add in section then: a statement 1 * userRepository.findByUsername(user.username) or 1 * userRepository.findByUsername(_ as String) I get an error UserDetailsServiceSpec.should find user and return new User:36 » UsernameNotFound. line 36 is in section when:


Answer:

You need to do the stubbing and verification in one step

then:
1 * userRepository.findByUsername(user.username) >> user

For details here is my answer from Predefined mock response in Spock:

Please refer to the documentation

When mocking and stubbing the same method call, they have to happen in the same interaction. In particular, the following Mockito-style splitting of stubbing and mocking into two separate statements will not work:

setup:
subscriber.receive("message1") >> "ok"

when:
publisher.send("message1")

then:
1 * subscriber.receive("message1")

As explained in Where to Declare Interactions, the receive call will first get matched against the interaction in the then: block. Since that interaction doesn’t specify a response, the default value for the method’s return type (null in this case) will be returned. (This is just another facet of Spock’s lenient approach to mocking.). Hence, the interaction in the setup: block will never get a chance to match.


When dealing with spring and transaction proxies you might also run into this problem https://github.com/spockframework/spock/issues/758

Question:

I want to Unit test a function in a service class.

The service class looks like this:

    class FooService{ 

    Double getFoo(SomeClass class){
      Double returningValue = 0.0
      String parameter

      SomeClass.findAllByCodeLike(parameter).each{
        Double amount = someOtherFooServiceMethod(it,it.someClassAttribute?.intValue())
        if(amount){
         returningValue += amount;
         }
       }
     }

    }



I know how to mock the SomeClass.findAllByCodeLike(parameter). I mocked it with the SomeClass.metaClass.static.findAllByCodeLike() and assign to this function a value.

But i don't know how to mock the .each{}. Is there way to do it?

By now I'm mocking a lot of domain classes to test this method and also save the domain classes into the in memory database. If i find a way to mock SomeClass.findAllByCodeLike(parameter).each{...} i can reduce the TestClass by a lot of code.


Answer:

You don't have to mock the each{}. You shoud simply make your findAllByCodeLike mock return a list of values:

SomeClass.metaClass.static.findAllByCodeLike = { code -> [ new SomeClass(...), new SomeClass(...), ... ] }
//...
SomeClass.findAllByCodeLike(parameter).each{ 
  doStuff it
}