Reputation: 23
I am trying out spock framework for writing unit test for a particular method in a service class but not able to mock response for a method.
Service class
@Service
@RequiredArgsConstructor
public class ServiceA {
private final RepositoryA repositoryA;
private final ServiceB serviceB;
public void methodUnderTest(String arg1,String arg2){
ObjectA objectA = repositoryA.someMethod(arg1, arg2); // here objectA is null
if (objectA != null) {
// Do something
}else{
// Do something else
}
}
}
I am not able to stub repositoryA.someMethod(arg1, arg2)
for some reason.
Its returning null while running the test.
I have tried putting both mock and stub interaction in then block as suggested in another question but its not working for me.
Test class
class ServiceATest extends Specification {
@Shared ServiceA serviceA;
@Shared RepositoryA repositoryA = Mock(RepositoryA)
@Shared ServiceB serviceB = Mock(ServiceB)
def "test"() {
given:
serviceA = new ServiceA(repositoryA, serviceB);
ObjectA mockResponse = ObjectA.builder()
.field1("demo")
.field2("demo")
.field3(Instant.now().plus(3, ChronoUnit.DAYS))
.build();
when:
serviceA.methodUnderTest(arg1,arg2);
then:
1 * repositoryA.someMethod(arg1,arg2) >> mockResponse // this is not working as expected
}
}
Upvotes: 0
Views: 41
Reputation: 67287
Spock manual, chapter Scope of Interactions:
Interactions are always scoped to a particular feature method. Hence they cannot be declared in a static method,
setupSpec
method, orcleanupSpec
method. Likewise, mock objects should not be stored in static or@Shared
fields.
Why would you even want to share a mock? Sharing state is bad test design and should only be used as sparingly as possible for expensive objects. Mocks are cheap. Simply resist the reflex to make everything @Shared
, especially mocks. This kind of premature optimisation is equivalent to shooting your own foot.
For reference, here is an MCVE reproducing your situation. It would have been your job to provide one:
package de.scrum_master.stackoverflow.q77973193
import spock.lang.Specification
import java.time.Instant
import java.time.temporal.ChronoUnit
class ServiceATest extends Specification {
ServiceA serviceA
RepositoryA repositoryA = Mock(RepositoryA)
ServiceB serviceB = Mock(ServiceB)
def "test"() {
given:
serviceA = new ServiceA(repositoryA, serviceB)
String arg1 = "one"
String arg2 = "two"
ObjectA mockResponse = ObjectA.builder()
.field1("demo")
.field2("demo")
.field3(Instant.now().plus(3, ChronoUnit.DAYS))
.build()
when:
serviceA.methodUnderTest(arg1, arg2)
then:
1 * repositoryA.someMethod(arg1, arg2) >> mockResponse
}
}
package de.scrum_master.stackoverflow.q77973193
class ServiceB {}
package de.scrum_master.stackoverflow.q77973193
class ServiceA {
RepositoryA repositoryA
ServiceB serviceB
ServiceA(RepositoryA repositoryA, ServiceB serviceB) {
this.repositoryA = repositoryA
this.serviceB = serviceB
}
void methodUnderTest(String s1, String s2) {
repositoryA.someMethod(s1, s2)
}
}
package de.scrum_master.stackoverflow.q77973193
import java.time.Instant
class RepositoryA {
ObjectA someMethod(String s1, String s2) {
ObjectA.builder()
.field1(s1)
.field2(s2)
.field3(Instant.now())
.build()
}
}
package de.scrum_master.stackoverflow.q77973193
import java.time.Instant
class ObjectA {
String field1
String field2
Instant field3
static Builder builder() {
new Builder()
}
static class Builder {
ObjectA instance = new ObjectA()
Builder field1(String value) {
instance.field1 = value
this
}
Builder field2(String value) {
instance.field2 = value
this
}
Builder field3(Instant value) {
instance.field3 = value
this
}
ObjectA build() {
instance
}
}
}
Try it in the Groovy Web Console.
Upvotes: 0