如何解决如何使用 SPOCK 框架为 HTTPBuilder 编写单元测试? 被测类Spock 规范控制台日志验证http.request(POST, TEXT) {...}的结果
我希望单元测试同时通过成功和失败执行路径。如何让测试用例走向成功或失败的路径?
void addRespondents()
{
http.request(POST,TEXT) {
uri.path = PATH
headers.Cookie = novaAuthentication
headers.Accept = 'application/json'
headers.ContentType = 'application/json'
body = respondentString
response.success = { resp,json ->
statusCode = 2
}
response.failure = { resp,json ->
if(resp.status == 400) {
statusCode = 3
def parsedJson = new JsonSlurper().parse(json)
}else{
autoCreditResponse = createErrorResponse(resp)
}
}
}
}
解决方法
好的,看来你使用了这个库:
<dependency>
<groupId>org.codehaus.groovy.modules.http-builder</groupId>
<artifactId>http-builder</artifactId>
<version>0.7.1</version>
</dependency>
因为我以前从未使用过 HTTPBuilder,而且它在使用 Groovy 时看起来是一个不错的工具,所以我尝试了一下,复制了您的用例,但将其转换为完整的 MCVE。我不得不承认这个库的可测试性很糟糕。甚至库本身的测试也不是适当的单元测试,而是集成测试,实际执行网络请求而不是模拟它们。该工具本身还包含测试模拟或有关如何测试的提示。
因为功能严重依赖于闭包中的动态绑定变量,模拟测试有点难看,我不得不查看工具的源代码才能实现它。好的黑盒测试基本上是不可能的,但这里是如何注入一个模拟 HTTP 客户端,返回一个预定义的模拟响应,其中包含足够的信息,不会使应用程序代码脱轨:
被测类
如您所见,我在类中添加了足够的数据来运行它并做一些有意义的事情。您的方法返回 void
而不是可测试的结果,而且我们只需要依赖测试副作用,这一事实并没有使测试更容易。
package de.scrum_master.stackoverflow.q68093910
import groovy.json.JsonSlurper
import groovyx.net.http.HTTPBuilder
import groovyx.net.http.HttpResponseDecorator
import static groovyx.net.http.ContentType.TEXT
import static groovyx.net.http.Method.POST
class JsonApiClient {
HTTPBuilder http = new HTTPBuilder("https://jsonplaceholder.typicode.com")
String PATH = "/users"
String novaAuthentication = ''
String respondentString = ''
String autoCreditResponse = ''
int statusCode
JsonSlurper jsonSlurper = new JsonSlurper()
void addRespondents() {
http.request(POST,TEXT) {
uri.path = PATH
headers.Cookie = novaAuthentication
headers.Accept = 'application/json'
headers.ContentType = 'application/json'
body = respondentString
response.success = { resp,json ->
println "Success -> ${jsonSlurper.parse(json)}"
statusCode = 2
}
response.failure = { resp,json ->
if (resp.status == 400) {
println "Error 400 -> ${jsonSlurper.parse(json)}"
statusCode = 3
}
else {
println "Other error -> ${jsonSlurper.parse(json)}"
autoCreditResponse = createErrorResponse(resp)
}
}
}
}
String createErrorResponse(HttpResponseDecorator responseDecorator) {
"ERROR"
}
}
Spock 规范
本规范涵盖了上述代码中响应的所有 3 种情况,使用返回不同状态代码的展开测试。
因为被测方法返回void
,我决定验证实际调用HTTPBuilder.request
的副作用。为此,我必须在 Spy
上使用 HTTPBuilder
。测试这个副作用是可选的,那么你就不需要间谍了。
package de.scrum_master.stackoverflow.q68093910
import groovyx.net.http.HTTPBuilder
import org.apache.http.HttpResponse
import org.apache.http.client.HttpClient
import org.apache.http.client.ResponseHandler
import org.apache.http.entity.StringEntity
import org.apache.http.message.BasicHttpResponse
import org.apache.http.message.BasicStatusLine
import spock.lang.Specification
import spock.lang.Unroll
import static groovyx.net.http.ContentType.TEXT
import static groovyx.net.http.Method.POST
import static org.apache.http.HttpVersion.HTTP_1_1
class JsonApiClientTest extends Specification {
@Unroll
def "verify status code #statusCode"() {
given: "a JSON response"
HttpResponse response = new BasicHttpResponse(
new BasicStatusLine(HTTP_1_1,statusCode,"my reason")
)
def json = "{ \"name\" : \"JSON-$statusCode\" }"
response.setEntity(new StringEntity(json))
and: "a mock HTTP client returning the JSON response"
HttpClient httpClient = Mock() {
execute(_,_ as ResponseHandler,_) >> { List args ->
(args[1] as ResponseHandler).handleResponse(response)
}
}
and: "an HTTP builder spy using the mock HTTP client"
HTTPBuilder httpBuilder = Spy(constructorArgs: ["https://foo.bar"])
httpBuilder.setClient(httpClient)
and: "a JSON API client using the HTTP builder spy"
def builderUser = new JsonApiClient(http: httpBuilder)
when: "calling 'addRespondents'"
builderUser.addRespondents()
then: "'HTTPBuilder.request' was called as expected"
1 * httpBuilder.request(POST,TEXT,_)
where:
statusCode << [200,400,404]
}
}
如果你已经使用过 Spock 一段时间,可能我不需要解释太多。如果您是 Spock 或模拟测试初学者,可能这有点太复杂了。但是 FWIW,我希望如果您研究代码,您可以了解我是如何做到的。我尝试使用 Spock 标签注释来解释它。
控制台日志
控制台日志表明规范涵盖了所有 3 个执行路径:
Success -> [name:JSON-200]
Error 400 -> [name:JSON-400]
Other error -> [name:JSON-404]
如果使用代码覆盖工具,当然不需要我在应用代码中插入的日志语句。它们仅用于演示目的。
验证http.request(POST,TEXT) {...}
的结果
为了规避你的方法返回void
的情况,你可以通过在spy交互中存根方法调用来保存HTTPBuilder.request(..)
的结果,首先通过原始结果,但也检查预期结果。
只需在 def actualResult
块的某处添加 given ... and
(在 when
中为时已晚),然后将 callRealMethod()
的结果分配给它,然后与 {{ 1}} 像这样:
expectedResult
如果您更喜欢数据表而不是数据管道, and: "a JSON API client using the HTTP builder spy"
def builderUser = new JsonApiClient(http: httpBuilder)
def actualResult
when: "calling 'addRespondents'"
builderUser.addRespondents()
then: "'HTTPBuilder.request' was called as expected"
1 * httpBuilder.request(POST,_) >> {
actualResult = callRealMethod()
}
actualResult == expectedResult
where:
statusCode << [200,404]
expectedResult << [2,3,"ERROR"]
块看起来像这样:
where
我认为这几乎涵盖了在这里测试有意义的所有内容。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。