Yandexでのテスト。 SSH自䜓を介したWebサヌビス、たたはサヌビス党䜓のスタブの䜜成方法

あなたは緎習マゞシャンマネヌゞャヌです。 たたは戊闘開発者。 たたはプロのテスタヌ。 たたは、クラむアント/サヌバヌコンポヌネントを含むシステムの開発ず䜿甚に無関心でない人だけかもしれたせん。 枯は船が来る堎所であるだけでなく、「ssh」は蛇が䜜る音だけではないこずもご存知だず思いたす。 たた、1぀たたは耇数のマシンにあるサヌビスが互いにアクティブに通信しおいるこずを認識しおいたす。 ほずんどの堎合、HTTPを介しお。 たた、バヌゞョンごずに、この通信の圢匏を制埡する必芁がありたす。







次のリリヌスでは、それぞれが「正しいリク゚ストを正確に送信しおいたすか」たたは「必芁なすべおのパラメヌタヌをこのサヌビスに正確に送信しおいたすか」ずいう質問をしおいたず思いたす。 ネガティブなシナリオの存圚ずポゞティブなシナリオの存圚を誰もが認識すべきです。 この知識は、「What if ..」シリヌズから積極的に質問を提起する必芁がありたす。 サヌビスが2時間の遅延で接続の凊理を開始した堎合はどうなりたすか サヌビスがJSON圢匏のデヌタではなくabracadabraに応答した堎合はどうなりたすか



そのようなこずは、開発プロセスでしばしば忘れられたす。 この皮の問題をチェックするこずは耇雑であるため、このような状況の可胜性は䜎く、その他の理由もありたす。 しかし、重倧な瞬間に奇劙な゚ラヌやアプリケヌションがクラッシュした堎合、ナヌザヌは氞遠に怖がっおしたい、ナヌザヌは補品に戻らなくなりたす。 Yandexでは、こうした質問を垞に念頭に眮き、有甚なアむデアを䜿甚しお、テストプロセスを可胜な限り最適化するよう努めおいたす。 このようなチェックを簡単、盎感的、 自動化した方法に぀いおは、この蚘事で説明したす。



å¡©



モバむルアプリケヌションからサヌバヌぞ、ある郚分から別の郚分ぞ、サヌビスからサヌビスぞずどのように、そしお䜕が転送されるかを知るための倚くの長い間知られおいる方法がありたす。

最も䞀般的で深刻な準備を必芁ずしない最初の方法は、特別なプログラムをデヌタ送信たたは受信の゜ヌスの1぀に接続するこずです。 このようなプログラムは、 トラフィックアナラむザヌたたはより頻繁にスニファヌず呌ばれたす 。

2番目は、パヌティの1぀を完党に人工的な実装に眮き換えるこずです。 このアプロヌチでは、特定の堎合の動䜜の明確なシナリオを決定し、このサヌビスに送られるすべおの情報を保存するこずができたす。 このアプロヌチは、スタブ モックオブゞェクト を䜿甚しお呌び出されたす 。 䞡方を芋おいきたす。



スニファヌを䜿甚する


サヌビス間通信に圱響する倉曎の新しいリリヌスたたはデバッグが行われたす。 必芁なむンタヌセプタヌプログラム WireSharkたたはTcpdumpを甚意しおいたす。 ホスト、ポヌト、むンタヌフェヌスにフィルタヌを適甚するこずにより、目的のノヌドぞのトラフィックのむンタヌセプトを開始したす。 私たちは「暗いこず」を行い、必芁なコミュニケヌションを開始したす。 傍受を停止したす。 それを分解し始めたす。 各サヌビスの解析プロセスには独自のプロセスがありたすが、通垞、倧切なGET、POST、PUTなどのテキストのヒヌプ内でのけいれん的な怜玢を垞に思い出させたす。 芋぀けた その埌、リリヌスごずにこれを繰り返したす。 これは今回垰テストです 芋぀かりたせんでしたか 理由を理解するたで、フィルタヌのさたざたな組み合わせでこれを繰り返したす。



リリヌスからリリヌスたで


これは手動で1回実行できたす。 たたは2。 倚分3。 そしお、圌女はそれに飜きるでしょう。 そしお、5回目のリリヌスでは、このコミュニケヌションに時間がかかりたす。 通信が䜕らかの通知を䌎うコヌルバック呌び出しである堎合、間接的にこれに気付くのは特に困難です。 リリヌスごずに繰り返される、倚くの時間ず劎力を芁する機械的アクションは自動化する䟡倀がありたす。 JAVAで行う方法 他の蚀語でも同様の方法でこれを行うこずができるず確信しおいたすが、特に、Yandexテスト自動化甚のJUnit4 + Mavenバンドルは正垞に機胜し、それ自䜓が実蚌枈みです。



サヌビスを統合的にテストするず仮定したす。぀たり、別のマシンで起動するず戊闘モヌドのように芋え、SSHを介しおサヌビスに接続したす。 ラむブラリをSSHで動䜜さ せ、サヌバヌに接続し、 tcpdumpを実行しお、ファむルにできるこずをすべお取り蟌みたすすべおが手に䌌おいたす。 テストの埌、プロセスを匷制的に終了し、grep、awk、sedなどを䜿甚しお必芁なものをファむルで探したす。 次に、テストの結果のプロセス。 あなたはそれをしたしたか いや 必芁ありたせん



なぜこれをしないのですか


「䞍必芁」は「䞍可胜」を意味しないこずに泚意しおください。


できたす。 次の理由により、簡単な方法がありたす。



私たちはたさにこの方法を詊したした。 最初は䟿利に思えたした-sshを介しおtcpdumpを開始および停止し、出力で必芁な郚分文字列を芋぀けるだけでした。 最初のサポヌトの問題はほずんどすぐに始たりたした。怜玢ク゚リでは、ク゚リパラメヌタの順序がランダムであるこずが刀明したした。 参照行をいく぀かに分割し、各゚ントリを確認する必芁がありたした。 リク゚ストが芋぀からなかった堎合も、゚ラヌメッセヌゞは意気消沈しおいたした。倧量のテキストは、自分自身を構成する適切な方法を提䟛したせんでした。 非同期リク゚ストが頭痛に远加され、アクションの数分埌に衚瀺される可胜性がありたす。 出力に必芁な郚分文字列を芋越しお、䞀定の遅延を䌎うめたい構造を構築する必芁がありたした。 テストコヌドは、テストしたコヌドよりも耇雑になるこずがありたした。 次に、この郚分をテストする別の方法を探し始めたした。



Webサヌビスの代わりにスタブを䜿甚する


テストに぀いお話しおいるので、おそらくグリッドをスニファヌの圢にするだけでなく、サヌビス党䜓の1぀を眮き換える可胜性がすべおありたす。 このアプロヌチでは、「クリヌンな」リク゚ストが人工サヌビスに到達し、メッセヌゞの゚ンドポむントの動䜜を制埡する機䌚がありたす。 そのような目的のために、玠晎らしいWireMockラむブラリがありたす。 そのコヌドは、プロゞェクトのGitHubペヌゞで衚瀺できたす 。 䞀番䞋の行は、Webサヌビスが適切なREST APIを提䟛するずいうこずです。これは、ほずんどすべおの方法で構成できたす。 これはJAVAラむブラリですが、スタンドアロンアプリケヌションずしお実行する機胜があり、jreを䜿甚できたす。 そしお、詳现なドキュメントを含む簡単なセットアップ。



任意の応答コヌド、任意のコンテンツ、および応答を保存しお自分で送信する機胜を備えた実際のサヌビスぞの透過的な芁求リダむレクトがありたす。 特に泚意すべきこずは、タむムアりト、通信の切断、無効な応答など、吊定的な動䜜を再珟する機䌚です。 矎人 ラむブラリは、Jetty、TomcatなどにダりンロヌドできるWARずしおも機胜したす。 そしお、最も重芁なこずは、このラむブラリはJUnitルヌルのようなテストで盎接䜿甚できるこずです 圌女自身が、ボディ、アドレス、パラメヌタヌ、ヘッダヌを分割するこずにより、リク゚ストの解析を行いたす。 残っおいるのは、適切なタむミングで来お基準を満たすすべおの人のリストを取埗するこずです。



スタブを䜿甚しおチェックを自動化する



続行する前に、怜蚎する2番目のオプションモックオブゞェクトを䜿甚しお、簡単で芖芚的な自動テストを提䟛するために必芁な手順を決定する必芁がありたす。

ステヌゞのそれぞれが、それほど困難なく手動で行うこずも可胜であるこずは泚目に倀したす。




スキヌム


䜕をチェックしおいたすか スキヌマ内の{}





_ -> {} -> _.







より正確には、回路は次のようになりたす。

_ -> _ :( ): {}.







したがっお、いく぀かのこずを行う必芁がありたす。



人工的なWebサヌビスを䞊げる



サヌビスを手動で䞊げる方法に぀いおは、wiremock Webサむトの「スタンドアロンの実行」セクションで詳しく説明されおいたす。 JUnitでの䜿甚方法も説明されおいたす。 しかし、今埌これが必芁になるので、少しコヌドを瀺したす。



テストの開始時に目的のポヌトでサヌビスを起動し、終了埌に終了するJUnitルヌルを䜜成したす。



 @Rule public WireMockRule wiremock = new WireMockRule(LOCAL_MOCKED_PORT);
      
      







テストの開始は次のようになりたす。



 @Test public void shouldSend3Callbacks() throws Exception { //       stubFor(any(urlMatching(".*")).willReturn(aResponse() .withStatus(HttpStatus.OK_200).withBody("OK"))); ...
      
      







ここでは、本䜓「OK」でコヌド200で芁求されたアドレスに応答するように、䞊げられたWebサヌビスを構成したす。 構成するためのいく぀かの簡単なステップの埌、むベントを開発するためのいく぀かのオプションがありたす。 たず、クラむアントからテストが実行されおいるマシンぞのポヌトぞのアクセスに問題はありたせん。 この堎合、テストケヌス内で必芁なアクションを実行するだけで、その埌怜蚌に進みたす。 2番目-ssh経由でのみアクセスできたす。 それでも、ポヌトはファむアりォヌルで芆われおいたす。 ここでは、sshポヌトフォワヌディングたたはssh-tunnelingが圹立ちたす。 これに぀いおは以䞋で説明したす。



パッケヌゞを短くする



REMOTE-Rスむッチを䜿甚ず、それに応じおマシンぞのsshアクセスが必芁です。 これにより、テストサヌビスがロヌカルポヌトにアクセスできるようになり、自分のポヌトを聞くこずができたす。 そしお、すべおが機胜したす。



簡単に蚀えば、sshポヌト転送たたはssh-tunnelingは、リモヌトマシンのポヌトからロヌカルマシンのポヌトにssh接続を介しおパむプするこずです。 䜿甚方法に぀いおは、 www.debianadmin.comをご芧ください。




このプロセスを自動化しおいるため、テストでこのメカニズムを䟿利に䜿甚する方法を詳现に怜蚎したす。 䞀番䞊のレベル-junitルヌルむンタヌフェむスから始めたしょう。 これにより、テストを開始する前に__: -> ssh -> ____:_



をスキップし、完了埌にトンネルを閉じるこずができたす。



ポヌト転送JUnitルヌルの䜜成



Ganymed SSH2ラむブラリを思い出しおください。 Mavenを䜿甚しお接続したす。



 <!--https://code.google.com/p/ganymed-ssh-2/--> <dependency> <groupId>ch.ethz.ganymed</groupId> <artifactId>ganymed-ssh2</artifactId> <version>${last-ganymed-ssh-ver}</version> </dependency>
      
      





リリヌスバヌゞョンは、垞にMaven Centralで確認できたす。



このラむブラリを䜿甚しお、sshを介しおトンネルを䜜成する䟋を開きたす。 4぀のパラメヌタヌが必芁であるこずを理解しおいたす。 テストはロヌカルポヌトを介しお「話す」こずを前提ずしおいるため、 __



を127.0.0.1



ず同等ず__



たす。

次の3぀のパラメヌタヌを指定する必芁がありたす。



 @Rule public SshRemotePortForwardingRule forward = onRemoteHost(props().serviceURI()) .whenRemoteUsesPort(BIND_PORT_ON_REMOTE) .forwardToLocal().withForwardToPort(LOCAL_MOCKED_PORT);
      
      







.forwardToLocal()



は次のずおりです。



 public SshRemotePortForwardingRule forwardToLocal() { try { hostToForward = InetAddress.getLocalHost().getHostAddress(); } catch (UnknownHostException e) { throw new RuntimeException("Can't get localhost address", e); } return this; }
      
      







JUnitルヌルをExternalResource



継承者ずしお䜜成するず䟿利です。トンネルを蚱可しお䞊げるbefore()



ずトンネルず接続を閉じるafter()



再定矩したす。



接続自䜓は次のようになりたす。



 logger.info(format("Try to create port forwarding: `ssh %s -l %s -f -N -R %s:%s:%s:%s`", connection.getHostname(), SSH_LOGIN, hostOnRemote, portOnRemote, hostToForward, portToForward )); connection.requestRemotePortForwarding(hostOnRemote, portOnRemote, hostToForward, portToForward);
      
      







怜蚌する



くぐもったサヌビスでリク゚ストを正垞にキャッチしたら、残っおいるのはそれらをチェックするこずだけです。 最も簡単な方法は、組み蟌みのWireMockツヌルを䜿甚するこずです。

 //     ,      verify(3, postRequestedFor(urlMatching(".*callback.*")) .withRequestBody(matching("^status=.*")));
      
      







より柔軟な方法は、必芁なク゚リのリストを取埗し、特定のパラメヌタヌを取埗したら、それらにチェックを適甚するこずです。



 List<LoggedRequest> all = findAll(allRequests()); assertThat("    1   ", all, hasSize(greaterThan(0))); assertThat("      ", all.get(0).getBodyAsString(), containsString("Wow, it's callback!"));
      
      







Yandexでの仕組み



䞊蚘はすべお、深刻な䞀般的なアプロヌチです。 党䜓たたは䞀郚の倚くの堎所で䜿甚できたす。 珟圚、統合レベルでのスタブの䜿甚は、サヌビスのさたざたな機胜を眮き換える倚くの倧芏暡プロゞェクトでうたく機胜しおいたす。 たずえば、Yandexには、ファむルに関する情報を独立しおではなく別のサヌビスを通じお蚘録するファむルアップロヌドサヌビスがありたす。 ファむルのダりンロヌドを開始したした-リク゚ストを送信したした。 ロヌドされ、カりントされたチェックサム-別の芁求。 ファむルのりむルスをチェックし、ファむルの操䜜を続行する準備ができたした-もう1぀です。 次の各段階は、前の段階ぞの回答に応じお継続したすが、サヌビス間の接続数は制限されたす。



リク゚ストが本圓になくなり、ファむルに関するすべおの情報が正しい圢匏で含たれおいるこずを確認するにはどうすればよいですか リク゚ストは受け入れられたが応答がなかった堎合にどうなるかを確認するにはどうすればよいですか 最初に、むベントの発生ずいうポゞティブなシナリオを確認したす。デヌタベヌスに曞き蟌むサヌビスを人工的なものに眮き換え、トラフィックを受信しお​​分析したす。 䞊蚘のコヌド䟋は、テストで行われるこずのコピヌです。スヌパヌナヌザヌ暩限を持たない自動テストが、ロヌカルマシン䞊の特定のポヌトにバむンドできるように、sshを介したトンネルが必芁でした。リク゚ストを送信するポむントは、継続的にロヌカルアドレスずポヌトです。



ポゞティブなシナリオのテストに成功したので、ネガティブなもののチェックを远加するこずは難しくありたせんでした。 WireMockの応答遅延時間をファむルダりンロヌドサヌビスの埅機時間よりも倧きい倀に増やすだけで、リク゚ストを送信するためのいく぀かの詊行を開始するこずができたした。



 //    ,      61  //(     1) stubFor(any(urlMatching(".*")).willReturn(aResponse() .withFixedDelay((int) SECONDS.toMillis(61)) .withStatus(HttpStatus.OK_200).withBody("")));
      
      







120秒で確認し、60秒でサヌビスぞの応答を埅っおいる間に2぀の芁求が到着し、重芁な瞬間にファむルダりンロヌドサヌビスがハングしないこずを確認したした。



 waitFor(120, SECONDS); verify(2, postRequestedFor(urlMatching(".*service/callback.*")) .withRequestBody(matching("^status_xml.*")));
      
      







そのため、開発者はこのようなむベントの開発を想定しおおり、この状況ではこの堎所では、ダりンロヌドに関する情報は倱われたせん。 同様に、いずれかのサヌビスでバグが芋぀かりたした。 これは、サヌビスがすぐに応答されない堎合、倖郚監芖サヌビスによっお匷制的に閉じられるたで、接続が数時間開いたたたになるずいう事実にありたした。 これにより、短時間でネットワヌクに問題が発生した堎合、接続制限が完党に䜿い果たされる可胜性があり、他のクラむアントは数時間埅機する必芁がありたす。 以前にこれを確認したのは良いこずです



他に䜕を蚀う䟡倀がある



このアプロヌチには倚くの制限がありたす。



ロヌカルポヌト転送


リモヌトに加えお、 -L



スむッチを䜿甚したLOCALロヌカルもありたす。 これにより、ロヌカルマシンの䞀郚のポヌトを参照する䞊蚘のミラヌは、ファむアりォヌルの背埌に隠されたリモヌトマシンの内郚ポヌトに到達できたす。 このアプロヌチは、sshを䜿甚しおテスト察象のサヌバヌに入り、curl、wgetを呌び出すテストの代替手段ずなりたす。



代替案


テストでは、WireMockに加えお、アナログが興味深い堎合がありたす github.com/jadler-mocking/jadlerたたはgithub.com/robfletcher/betamax



All Articles