我正在为我的java应用程序构建一个groovy rest客户端,以用于测试自动化。我最初在httpBuilder中编写了该服务,但无法弄清楚如何解析响应。在非200响应中,我得到了一个异常,我可以捕获并断言该消息。找不到,坏请求等。更新后,我可以解析响应,但是当我得到非200响应时,它会尝试将其解析为我的对象,然后抛出无用的“missingProperty”异常。该文档展示了如何使用response.parser <CONTENT_TYPE>, { config, fs ->...}
解析响应,以及如何使用response.success{fs -> ...}
或response.when(<CODE>){fs -> ...}
分支状态代码,而不是如何仅为成功解析并使用不同的逻辑失败。我目前的代码如下:
import groovyx.net.http.ChainedHttpConfig
import groovyx.net.http.FromServer
import groovyx.net.http.HttpBuilder
import groovyx.net.http.NativeHandlers
import static groovyx.net.http.ContentTypes.JSON
import static groovyx.net.http.NativeHandlers.Parsers.json
class CarClient {
private final HttpBuilder http
CarClient() {
http = HttpBuilder.configure {
request.uri = "localhost:8080"
request.encoder JSON, NativeHandlers.Encoders.&json
}
}
List<Car> getCars(make) {
http.get(List) {
request.uri.path = "/cars/make/${make}"
response.failure { fs ->
println("request failed: ${fs}")
}
response.parser JSON, { ChainedHttpConfig config, FromServer fs ->
json(config, fs).collect { x -> new Car(make: x."make", model: x."model") }
}
}
}
}
class Car {
def make
def model
}
然后我的spock测试:
def "200 response should return list of cars"() {
when:
def result = client.getCars("honda")
then:
result.size == 3
result[0].make == "honda"
result[0].model == "accord"
}
def "404 responses should throw exception with 'not found'"() {
when:
client.getCars("ford")
then:
final Exception ex = thrown()
ex.message == "Not Found"
}
在旧版本下,第一次测试失败,第二次测试通过。在新版本下,第一次测试通过,第二次测试失败。我也从未真正看到request failed:...
消息。我刚收到groovy.lang.MissingPropertyException
。当我单步执行时,我可以看到它尝试将not found
响应加载为Car对象。
额外奖励:为什么我必须使用显式属性映射而不是文档中的常规转换?
json(config, fs).collect { x -> x as Car }
更新 - 为了澄清,这不是我的实际来源。我正在使用在WAS上运行的专有内部API,我无法完全控制它。我正在编写API的业务逻辑,但使用WAS和我无法访问的专有库正在编组/解组响应。名称已被更改,以保护无辜/我的工作。这些是我从最初的帖子以来尝试过的解决方法:
这会在非200响应上正确触发故障块,但解析失败并出现IO - stream closed
错误。此外,我在故障块中抛出的任何异常都被包装在RuntimeException中,这阻止了我访问信息。我已经尝试将它包装在文档中建议的传输异常中,但是当我得到它时它仍然是一个RuntimeException。
List<Car> getCars(make) {
http.get(List) {
request.uri.path = "/cars/make/${make}"
response.failure { fs ->
println("request failed: ${fs}")
throw new AutomationException("$fs.statusCode : $fs.message")
}
response.success { FromServer fs ->
new JsonSlurper().parse(new InputStreamReader(fs.getInputStream, fs.getCharset())).collect { x -> new Car(make: x."make", model: x."model") }
}
}
}
}
这一个正确解析了200个带有条目的响应,200个没有条目仍然抛出缺少的属性异常。与之前的impl一样,AutomationException被包装,因此无用。
List<Car> getCars(make) {
http.get(List) {
request.uri.path = "/cars/make/${make}"
response.parser JSON, { ChainedHttpConfig config, FromServer fs ->
if (fs.statusCode == 200) {
json(config, fs).collect { x -> new Car(make: x."make", model: x."model") }
} else {
throw new AutomationException("$fs.statusCode : $fs.message")
}
}
}
}
关于奖金,我所遵循的指南显示了json(config, fs)
输出到Car对象的隐式转换。我必须明确设置新对象的道具。没什么大不了的,但是我想知道我是否错误地配置了其他东西。
答案 0 :(得分:1)
您可以在failure
处理程序中抛出异常,它将执行您要查找的内容:
response.failure { fs ->
throw new IllegalStateException('No car found')
}
我不确定您要测试的服务器是什么,所以我使用Ersatz编写了一个测试:
import com.stehno.ersatz.ErsatzServer
import spock.lang.AutoCleanup
import spock.lang.Specification
import static com.stehno.ersatz.ContentType.APPLICATION_JSON
class CarClientSpec extends Specification {
@AutoCleanup('stop')
private final ErsatzServer server = new ErsatzServer()
def 'successful get'() {
setup:
server.expectations {
get('/cars/make/Toyota').responder {
content '[{"make":"Toyota","model":"Corolla"}]', APPLICATION_JSON
}
}
CarClient client = new CarClient(server.httpUrl)
when:
List<Car> cars = client.getCars('Toyota')
then:
cars.size() == 1
cars.contains(new Car('Toyota', 'Corolla'))
}
def 'failed get'() {
setup:
server.expectations {
get('/cars/make/Ford').responds().code(404)
}
CarClient client = new CarClient(server.httpUrl)
when:
client.getCars('Ford')
then:
def ex = thrown(IllegalStateException)
ex.message == 'No car found'
}
}
请注意,我必须让您的客户端具有可配置的基本网址(并且Car
需要@Canonical
注释)。如果您还没有阅读有关Take a REST with HttpBuilder-NG and Ersatz的博文,我建议您这样做,因为它提供了一个很好的概述。
我不确定你的奖金问题是什么意思。