2017-11-01 6 views
0

テスト自動化で使用するJavaアプリケーション用のGroovy Restクライアントを構築しています。私はもともとhttpBuilderでサービスを書いていましたが、レスポンスを解析する方法を理解できませんでした。非200の応答では、私は捕捉してメッセージに主張することができる例外があります。更新後、私はレスポンスを解析することができますが、非200レスポンスを取得すると、それをオブジェクトとして解析して、役に立たない 'missingProperty'例外をスローします。このドキュメントでは、response.parser <CONTENT_TYPE>, { config, fs ->...}を使用して応答を解析する方法と、response.success{fs -> ...}またはresponse.when(<CODE>){fs -> ...}を使用してステータスコードを分岐する方法について説明しますが、成功のためにのみ解析する方法と、次のように私の現在のコードは次のとおりです。HttpBuilder-ngを使用して成功を解析したりエラーをスローするには

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" 
} 

古いバージョンでは、最初のテストは失敗し、第二の試験は合格しました。新しいバージョンでは、最初のテストは合格し、2番目のテストは失敗します。私は実際にはrequest failed:...メッセージも見たことがありません。私はちょうどgroovy.lang.MissingPropertyExceptionを得る。私がステップスルーすると、not foundの応答をCarオブジェクトとしてロードしようとしているのがわかります。

ボーナス:なぜドキュメントのようにGroovyキャストの代わりに明示的なプロパティマッピングを使用する必要がありますか?

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") 
      } 
     } 
    } 
} 

、私は次のようれたガイドは、Carオブジェクトにjson(config, fs)出力の暗黙のキャストを示しました。私は明示的に新しいオブジェクトの小道具を設定する必要があります。大したことではありませんが、私は別のものを間違って設定したかどうか疑問に思います。

答えて

0

あなたはfailureハンドラで例外を投げることができる、それはあなたが探しているものを行います:私はあなたがに対してテストしているか、サーバわからない

response.failure { fs -> 
    throw new IllegalStateException('No car found') 
} 

ので、私はErsatzを使用してテストを書いた:

私はあなたのクライアントは、構成ベースURLを持たなければならなかった(と Car@Canonical注釈が必要)
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' 
    } 
} 

注意。 Take a REST with HttpBuilder-NG and Ersatzについてのブログ投稿を読んでいない場合、私はそれが良い概観を提供するので、そうすることを提案します。

私はあなたのボーナスの質問に何を意味するのか分かりません。

+0

@ thejames42これで問題は解決しますか? – cjstehno

+0

残念ながら。問題は、応答がいつもコンテンツタイプ 'application/json'で返ってくるということだと思うので、パーサは常に起動されます。私はパーサーを削除して成功クローズで '新しいJSONSlurper(... fs.getIntputStream)を呼び出そうとしましたが、 'stream closed'エラーがスローされます。私がこれまでに持っているベストは、ネイティブハンドラを呼び出す前にパーサークロージャでtry catchを追加していますが、例外処理は混乱しています。テストをビルドしようとしていますが、それはサイドプロジェクトなので、作業する時間はそれほどありません。 – thejames42

関連する問題