Spock - 模拟外部服务
Spock - mocking external service
我是 spock 框架测试的新手,我没有找到任何可以找到所需信息的示例。因此,我认为最好的方法是展示一个我需要的例子。
例如在 spock 中测试 class:
def "getData" (){ // this is test of getData method from ExternalService
when:
Result result = externalService.getData()
then:
result.msg = 'SUCCESS'
}
服务Class:
public class ExternalService(){
private ServiceConnector serviceConnector;
public Result getData(){
Result result = serviceConnector.callAndGet();
prepareInformation(data);
updateStatuses(data);
return result;
}
}
Class 数据作为域 Class:
public class Data {
private String msg
private int Id
// +getters/setters
}
现在我有 getData
测试并且想模拟唯一的方法 callAndGet()
。这意味着每次我调用 callAndGet
时,我都需要有带有 msg SUCCESS 的对象数据,但应该正常调用 getData
方法中的所有其他方法。
是不是很好理解?问题是我们如何 inject/mock 服务 class ExternalService
进入 spock 测试 class?
你需要做的是模拟 ServiceConnector
class 并通过构造函数传递它(例如)。见下文:
@Grab('org.spockframework:spock-core:1.0-groovy-2.4')
@Grab('cglib:cglib-nodep:3.1')
import spock.lang.*
class Test extends Specification {
def 'some spec'() {
given:
def serviceConnector = Mock(ServiceConnector) {
callAndGet() >> new Result(msg: 'SUCCESS')
}
def externalService = new ExternalService(serviceConnector)
when:
Result result = externalService.getData()
then:
result.msg == 'SUCCESS'
}
}
public class ExternalService {
private ServiceConnector serviceConnector
public ExternalService(ServiceConnector serviceConnector) {
this.serviceConnector = serviceConnector
}
public Result getData() {
Result result = serviceConnector.callAndGet()
prepareInformation(result)
updateStatuses(result)
result
}
private void prepareInformation(Result data) {
}
private void updateStatuses(Result data) {
}
}
public class ServiceConnector {
public Result callAndGet() {
}
}
public class Result {
String msg
}
您不应该试图嘲笑您的服务 "only one method"。这只是糟糕设计的标志,您的代码不可测试。您应该更好地隔离 class 与小型服务的依赖关系,并在单元测试中模拟此服务。然后通过集成测试和依赖项的完整实现来测试上层。
在您的示例中,ServiceConnector
应该是一个可以轻松模拟的接口。有了这个条件,测试可以写成:
def "test a mocked service connector"() {
given: "a service connector"
def serviceConnector = Mock(ServiceConnector)
and: "an external service"
def externalService = new ExternalService(serviceConnector:serviceConnector)
when: "Data is loaded"
def result = externalService.data
then: "ServiceConnector is called"
serviceConnector.callAndGet() >> new Result(msg:"SUCCESS")
and: "Data is mocked"
result.msg == "SUCCESS"
}
但是,如果 ServiceConnector 是 class 并且您无法更改它,则可以在 Spock 中使用部分模拟。这种测试很难维护,并且会产生很多副作用:
def "test a mocked service connector"() {
given: "a service connector"
def serviceConnector = Spy(ServiceConnector) {
callAndGet() >> new Result(msg:"SUCCESS")
}
and: "an external service"
def externalService = new ExternalService(serviceConnector:serviceConnector)
when: "Data is loaded"
def result = externalService.data
then: "Data is mocked"
result.msg == "SUCCESS"
}
我是 spock 框架测试的新手,我没有找到任何可以找到所需信息的示例。因此,我认为最好的方法是展示一个我需要的例子。
例如在 spock 中测试 class:
def "getData" (){ // this is test of getData method from ExternalService when: Result result = externalService.getData() then: result.msg = 'SUCCESS' }
服务Class:
public class ExternalService(){ private ServiceConnector serviceConnector; public Result getData(){ Result result = serviceConnector.callAndGet(); prepareInformation(data); updateStatuses(data); return result; } }
Class 数据作为域 Class:
public class Data { private String msg private int Id // +getters/setters }
现在我有 getData
测试并且想模拟唯一的方法 callAndGet()
。这意味着每次我调用 callAndGet
时,我都需要有带有 msg SUCCESS 的对象数据,但应该正常调用 getData
方法中的所有其他方法。
是不是很好理解?问题是我们如何 inject/mock 服务 class ExternalService
进入 spock 测试 class?
你需要做的是模拟 ServiceConnector
class 并通过构造函数传递它(例如)。见下文:
@Grab('org.spockframework:spock-core:1.0-groovy-2.4')
@Grab('cglib:cglib-nodep:3.1')
import spock.lang.*
class Test extends Specification {
def 'some spec'() {
given:
def serviceConnector = Mock(ServiceConnector) {
callAndGet() >> new Result(msg: 'SUCCESS')
}
def externalService = new ExternalService(serviceConnector)
when:
Result result = externalService.getData()
then:
result.msg == 'SUCCESS'
}
}
public class ExternalService {
private ServiceConnector serviceConnector
public ExternalService(ServiceConnector serviceConnector) {
this.serviceConnector = serviceConnector
}
public Result getData() {
Result result = serviceConnector.callAndGet()
prepareInformation(result)
updateStatuses(result)
result
}
private void prepareInformation(Result data) {
}
private void updateStatuses(Result data) {
}
}
public class ServiceConnector {
public Result callAndGet() {
}
}
public class Result {
String msg
}
您不应该试图嘲笑您的服务 "only one method"。这只是糟糕设计的标志,您的代码不可测试。您应该更好地隔离 class 与小型服务的依赖关系,并在单元测试中模拟此服务。然后通过集成测试和依赖项的完整实现来测试上层。
在您的示例中,ServiceConnector
应该是一个可以轻松模拟的接口。有了这个条件,测试可以写成:
def "test a mocked service connector"() {
given: "a service connector"
def serviceConnector = Mock(ServiceConnector)
and: "an external service"
def externalService = new ExternalService(serviceConnector:serviceConnector)
when: "Data is loaded"
def result = externalService.data
then: "ServiceConnector is called"
serviceConnector.callAndGet() >> new Result(msg:"SUCCESS")
and: "Data is mocked"
result.msg == "SUCCESS"
}
但是,如果 ServiceConnector 是 class 并且您无法更改它,则可以在 Spock 中使用部分模拟。这种测试很难维护,并且会产生很多副作用:
def "test a mocked service connector"() {
given: "a service connector"
def serviceConnector = Spy(ServiceConnector) {
callAndGet() >> new Result(msg:"SUCCESS")
}
and: "an external service"
def externalService = new ExternalService(serviceConnector:serviceConnector)
when: "Data is loaded"
def result = externalService.data
then: "Data is mocked"
result.msg == "SUCCESS"
}