Как я могу разобрать успех или выбросить ошибку с HttpBuilder-ng
Я создаю отличный клиент для моего java-приложения для автоматизации тестирования. Первоначально я написал сервис в 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
}
тогда мои спок тесты:
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
ответ как объект автомобиля.
Бонус: почему я должен использовать явное сопоставление свойств, а не приведение к groovy, как в документе?
json(config, fs).collect { x -> x as Car }
обновление - для ясности, это не мой фактический источник. Я использую собственный внутренний API-интерфейс, работающий на WAS, который я не полностью контролирую. Я пишу бизнес-логику 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. Я должен явно установить реквизит нового объекта. Ничего страшного, но это заставляет меня задуматься, неправильно ли я что-то настроил.
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
аннотация). Если вы еще не читали сообщение в блоге о REST с HttpBuilder-NG и Ersatz, я бы посоветовал сделать это, поскольку он дает хороший обзор.
Я не уверен, что вы имеете в виду в своем бонусном вопросе.