我正在尝试使用 spock 框架为服务类中的特定方法编写单元测试 但无法模拟方法的响应。
服务等级
@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
}
}
}
由于某种原因,我无法存根
repositoryA.someMethod(arg1, arg2)
。
运行测试时返回 null。
我尝试按照另一个问题中的建议将模拟和存根交互放入 then 块中,但它对我不起作用。
测试课
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
}
}
Spock 手册,章节交互范围:
交互始终局限于特定的功能方法。因此,它们不能在静态方法、
方法或setupSpec
方法中声明。同样,模拟对象不应存储在静态或cleanupSpec
字段中。@Shared
你为什么想分享一个模拟?共享状态是糟糕的测试设计,对于昂贵的对象应该尽可能少地使用。模拟很便宜。只需抵制反射即可使一切变得
@Shared
,尤其是嘲笑。这种过早的优化就等于搬起石头砸自己的脚。
作为参考,这里有一个 MCVE 再现了您的情况。你的工作就是提供一个:
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
}
}
}
在 Groovy Web Console 中尝试一下。