APIサービスとRSpecのテスト

画像






しばしばプロトタイプの形で、小さなAPIサービスを作成する必要がある場合があります。 そして、多くの場合、このプロトタイプは「作品-触らない」という原則に従って、元の書面のままです。 比較的小さなサービスであっても、書き直しにはエラーが発生したり、すぐにはわからないふるまいを誤って変更したりする可能性があります。 ブラックボックステスト(機能テスト)がここで役立ちます。 テストの作成は開発プロセスの重要な部分であり、テストの作成に費やされる時間は、テストされた機能の実装よりもはるかに長くなります。 テスト対象のコード(サービス)と自動テストが異なるプログラミング言語で記述されている場合、テスト方法を検討することを提案します。 このアプローチにより、最初に選択したテクノロジーに依存せずにテストを作成できるため、プロトタイプを「破棄」し、必要な機能を他のテクノロジーに簡単に書き直すことができます。 さらに、これは、テスト対象のサービスと同じ言語でテストを作成する必要がないことを示すデモです。



たとえば、次のタスクを実行します。 次のメソッドを使用してhttp APIサービスを作成します。



  1. GET / ping-サービスは常にコード200とテキスト「OK」で応答する必要があります。
  2. GET / movies-このサービスは映画のリストを提供し、そのリストはサードパーティのサービスから受け取ります。 ratingパラメーターは、クエリを介したフィルター処理をサポートします;パラメーターが指定されていない場合、既定値を使用します。


必要なもの:





重要:このテキストでは、問題のツールのインストール、構成、および使用に関する詳細は意図的に省略されています



ルビー言語構文を使用すると、少なくとも実用的なコードでかなり簡潔なテストを作成できるため、Rspecがテストフレームワークとして選択されました。 MockServerは、サードパーティのサービスからの応答をエミュレートするための非常に強力なツールです。主な機能は、独立したhttp APIサービスとして起動できることです。 別のテクノロジースタックを使用する場合、ほぼ確実に最も便利なアナログを見つけることができます。 これらのツールは、例としてのみ使用されます。



ruby、java、golangのインストールと設定の手順はスキップします。 rspecから始めましょう。 便宜上、バンドラーをインストールすることをお勧めします。 使用される宝石のリストは次のとおりです。



gem "rspec" gem "rest-client" gem "mockserver-client"
      
      





Mockserverには、かなり便利なREST APIと、JavaおよびJavaScript用のクライアントがあります。 rubyクライアントを使用します。現時点では明らかにサポートされていませんが、基本的な機能は利用できます。 コマンドを使用してアプリケーションスケルトンを生成する
  rspec --init 




次に、ファイル/spec/api_spec.rbを作成します。



 # /spec/api_spec.rb require 'spec_helper' require 'rest-client' require 'mockserver-client' RSpec.describe "ApiServer" do let(:api_server_host) { "http://#{ENV.fetch("API_SERVICE_ADDR", '127.0.0.1:8000')}" } end
      
      





/ pingメソッドのテストを作成しましょう(このコードの一部をRSpec.describe“ ApiServer”ブロックに入れます)



 describe "GET /ping" do before { @response = RestClient.get "#{api_server_host}/ping" } it do expect(@response.code).to eql 200 expect(@response.body).to eql 'OK' end end
      
      





ここで(rspecコマンドを使用して)テストを実行すると、予想どおりにエラーで失敗します。 メソッドの実装を作成します。



 package main import (    "net/http"    "github.com/labstack/echo" ) func main() {    e := echo.New()    e.GET("/ping", ping)    e.Start(":8000") } func ping(c echo.Context) error {    return c.String(http.StatusOK, "OK") }
      
      





APIサービスをコンパイルして実行します(たとえば、go runを使用)。 コードを簡素化するために、サービスとテストを手動で実行します。 まずIPAサービスを開始し、次にrspecを開始します。 今回は、テストが正常にパスするはずです。 したがって、最も単純な独立したテストを取得しました。これにより、任意の言語またはサーバーでこのAPIメソッドの実装をテストできます。



例を複雑にして、2番目のメソッド-/ moviesを追加しましょう。 テストコードを追加します。



取得/映画
 describe "GET /movies" do let(:params) { {} } before { @response = RestClient.get "#{api_server_host}/movies", {params: params} } context '?rating=X' do let(:params) { {rating: 90} } let(:query_string_parameters) { [parameter('rating', '90')] } let(:movies_resp_body) { File.read('spec/fixtures/movies_90.json') } let(:resp_body) { movies_resp_body } include_examples 'response_ok' end describe 'set default filter' do let(:query_string_parameters) { [parameter('rating', '70')] } let(:movies_resp_body) { File.read('spec/fixtures/movies.json') } let(:resp_body) { movies_resp_body } include_examples 'response_ok' end end
      
      







タスクの条件により、映画のリストをサードパーティAPIから取得する必要があります。サードパーティAPIへの応答をエミュレートするには、モックサーバーを使用します。 これを行うために、私たちは彼に応答本文と彼がそれらに答える条件を与えます。 これは次のように実行できます。



セットアップモック
 include MockServer include MockServer::Model::DSL def create_mock_client MockServer::MockServerClient.new(ENV.fetch("MOCK_SERVER_HOST", 'localhost'), ENV.fetch("MOCK_SERVER_PORT", 1080)) end let(:query_string_parameters) { [] } let(:movies_resp_body) { '[]' } before :each do @movies_server = create_mock_client @movies_server.reset @exp = expectation do |exp| exp.request do |request| request.method = 'GET' request.path = '/movies' request.headers << header('Accept', 'application/json') request.query_string_parameters = query_string_parameters end exp.response do |response| response.status_code = 200 response.headers << header('Content-Type', 'application/json; charset=utf-8') response.body = body(movies_resp_body) end end @movies_server.register(@exp) end
      
      







APIサービスでのハンドラーの実装:



映画ハンドラー
 func movies(c echo.Context) error { rating := c.QueryParam("rating") if rating == "" { rating = "70" } client := &http.Client{} req, _ := http.NewRequest("GET", "http://localhost:1080/movies", nil) req.Header.Add("Accept", `application/json`) q := req.URL.Query() q.Add("rating", rating) req.URL.RawQuery = q.Encode() if resp, err := client.Do(req); err != nil { panic(err) } else { return c.Stream(http.StatusOK, "application/json", resp.Body) } }
      
      







テストを実行するには、3つのプロセスを開始する必要があります:チェックされたサービス、模擬サーバー、およびrspec。



 go run main.go java -jar mockserver-netty-5.3.0-jar-with-dependencies.jar -serverPort 1080 rspec
      
      





このプロセスの自動化は別のタスクです。



また、サービスコードの合計サイズとそのテストに注意する価値があります。 最小限のサービスをテストで30行でカバーするには、テストの3倍近くのコード行が必要であり、Mookをインストールするための膨大なコードが必要ですが、起動および応答フィクスチャの自動化は考慮されていません。 一方で、これは合理性のテストの問題を提起します;一方では、この比率は一般に標準であり、良いテストが少なくとも作業の半分であることを示します。 また、選択した元のテクノロジーからの独立性は大きなプラスになります。 ただし、この方法では、データベースの状態をテストすることは非常に難しいことに気付くのは簡単です。 この問題の可能な解決策の1つは、データベースの状態を変更したり、さまざまな状況に合わせてデータベーススナップショット(フィクスチャ)を作成したりするためのプライベートAPIを追加することです。



リストの要旨



議論、賛否両論、批判-私たちはコメントで待っています



All Articles