狠狠撸

狠狠撸Share a Scribd company logo
マイクロサービスの開発と
テストファースト/テスト駆動開発
柴田芳樹/Yoshiki Shibata
Software Engineer@merpay
自己绍介
自己绍介
柴田 芳樹(Yoshiki Shibata) 1959年11月生まれ
九州工業大学 情報工学科&大学院(情報工学)
職務経歴
● 富士ゼロックス(株)(Xerox PARCを含め米国ゼロックス社に4年半駐在)
● 日本オラクル(株)
● (株)ジャストシステム
● 富士ゼロックス情報システム(株)(米国ゼロックス社に半年駐在)
● (株)リコー
● ソラミツ(株)
● (株)メルペイ(2018年6月~現在)
主な著書および翻訳本
● 『プログラマー”まだまだ”現役続行』
● 『Effective Java 第3版』
● 『プログラミング言語Go』
● 『ベタープログラマ』
1990年代までのソフトウェアテスト
テスト駆動開発は2000年前後に生まれている(1)
1980年代?1990年代までは、自動化されたテストというのは常識ではなく、テストは手作業で実行さ
れ、結果の確認は目視が主流であった。
テストの実行は確かに簡単になりました。しかし、テストの実行が簡単になっても、
テストは依然として極めて退屈なものでした。これは、コンソールに出力されるテス
ト結果を私がチェックしなければならないためです。
この10年の間に、この業界では多くのことがありました。1997年当時、テスト駆動
開発などという言葉は、誰も聞いたことがありませんでした。ほとんどの人にとっ
て、単体テストというのは動作をひとたび「確認」したら捨ててしまうものでした。苦
労してクラス、メソッドを書き上げ、それらをテストするための、その場しのぎのコー
ドをでっちあげていたのです。
テスト駆動開発は2000年前後に生まれている(2)
― デバッグの話をしましょう。あなたが追いかけた最悪のバグはどんなものでしたか
ブロック(Joshua Bloch): 思いつくものの一つは、 …
???
― つまりバグはあなたのコードにはなかったのに、自分のコードに詳細なユニットテストを書
いてテストし、自分のコード以外に目を向ける以外なくなったわけですね。そのミューテックス
の作者がテストを書いていればバグは見つかったはずで、自分が一週間半デバッグすること
はなかったのにと思いますか?
ブロック: あのミューテックス機構に良い自動化ユニットテストがあれば、あの苦痛は避けら
れたとは思いますが、頭に入れておく必要があるのは、これが90年代初期の話だということ
です。十分なユニットテストを書いていないということでそのエンジニアを非難しようという気
は全く起きませんでした。
テスト駆动开発の経験
2000年までのソフトウェア開発経験
ワークステーション
● Fuji Xerox 6060 Workstation(C言語、1986年)
● Fuji Xerox GlobalView Workstation(Mesa言語、1992年)
デジタル複合機のコントローラソフトウェア
● Fuji Xerox DocuStation IM 200 / AS200 (C++言語、1996年/1995年)(Take-1)
● Fuji Xerox DocuCentreシリーズの最初の製品(C++言語、2001年)(Take-2)
テスト駆动开発の経験
デジタル複合機のコントローラソフトウェア開発
● Take-3: 2003年2月?2009年8月
○ Linux / C++、マルチプロセス、マルチスレッド
● Take-4:2013年7月?2015年5月
○ Linux / Go、マルチプロセス、ゴルーチン
どちらも、実機レス開発を可能にする完全なテスト駆動開発
(初めての)ウェブサービス开発蔼メルペイ
マイクロサービスの構成(簡略版)@メルペイ
● クライアント(iOSアプリやAndroidアプ
リ)はgatewayを通して、APIマイクロ
サービスと通信する
● APIマイクロサービスは、他のマイクロ
サービスを使って機能を実現する
開発の基本フロー
API仕様の記述
テストフレームワーク作成
テストコード作成
機能の実装
リファクタリング
課題:依存しているマイク
ロサービスも含めて、多く
のマイクロサービスの並
行開発が行われるなか
で、機能実装とテストを完
了させなければならな
かった。
API仕様に求められる内容
どのようなAPI仕様であっても、そこに記述されなければならない事柄は基本的に同じです。
● 提供される機能の説明
● 関数呼び出し、メソッド呼び出し、あるいは、RPC呼び出しでのパラメータの意味と正当な値の範囲
● 呼び出されるための事前の状態に何らかの制約があったり、呼び出し順序に何らかの制約があったりするの
であれば、どのような制約なのか
そして、防御的プログラミング(defensive programming)の観点から、以下の事柄もきちんと仕様に記述され
ている必要があります。
1. 不正なパラメータ値が渡された場合、どのようなエラーが返される(あるいは例外がスローされる)のか
2. 不正な状態や不正な順序で呼び出された場合、どのようなエラーが返される(あるいは例外がスローされる)
のか
「gRPCを用いたマイクロサービスのAPI仕様の記述」(https://tech.mercari.com/entry/2019/05/31/040000)
gRPC:.protoファイルにAPI仕様を記述
/**
* `Greeter` service provides a way to say a greeting message to a user and
* returns a message from the user.
* - Each RPC may return `Internal` but it is not listed in the `[ERRORS]` section for brevity.
*/
service Greeter {
// Sends a greeting message to a user and returns a message from the user.
rpc SayHello (HelloRequest) returns (HelloResponse) {}
}
/**
* `Hello` sends say a hello message to a user and returns a message from
* the user.
* - the returned message may be empty.
*
* [ERRORS]
* - InvalidArgument:
* - `name` is empty or too long
* - NotFound:
* - the user specified by `name` is not found in the system
*/
message HelloRequest {
string name = 1;
}
message HelloResponse {
string message = 1;
}
サービスの説明
RPCの簡単な説明
RPCの詳細な説明
エラーの説明
書きたいテスト
func TestXXX(t *testing.T) {
// prepare
// 依存しているサービスに対して呼び出す
// RPCごとに、返すレスポンスあるいは
// エラーの設定。(スタブとして設定)
...
// action
// テストするサービスのRPC呼び出し。
...
// check
// 呼び出した結果のレスポンスの検査。
...
}
func TestYYY(t *testing.T) {
// prepare
// 依存しているサービスに対して呼び出すRPCへ正しくパラ
// メータが渡されたかを検査あるいはパラメータを保存する
// 設定。(モックとして設定)
...
// action
// テストするサービスのRPC呼び出し。
...
// check
// 依存したサービスのRPCへ正しくパラメータが渡されたかの
// 検査。
// 呼び出した結果のレスポンスの検査。
...
}
テストフレームワークの構築(概要)
テスト対象のマイクロサービスは、別プロセ
スとして起動
○ 依存するマイクロサービスは、起動時の
環境変数の設定によりTest Suite のプロ
セスへ接続するようになっている
依存しているマイクロサービス
のFake版をgRPCから自動生
成
○ RPCごとに返すべきレスポ
ンス/エラーを設定できる
○ テストコードと同じプロセス内
でgRPCのサーバーとして動
作する
テストフレームワークの構築(テスト開始/終了の同期)
加盟店管理APIサービス Test Suite
 サービス起動 
 サービスReady通知   
テ
ス
ト
実
施
  サービス終了指示 
 サービス終了待ち 
サービスが終了す
るとカバレッジ情報
を保存する
依存サービスFake準備
テストフレームワークの構築(外部サービスのFake)
メルペイのマイクロサービス以外の依存している外部サービスもFakeする
● APIが公開されていることが多い。Go言語用アクセスライブラリであれば、アクセスするための
ソースコードも調べることができるので、独自にFakeするのは難しくはない。
● テスト対象のマイクロサービスから外部サービスのAPIが正しく呼び出されたかをテストコードで
検査する
● GCPのPubSubのPublisher/Subscriber(gRPC)
○ Go言語用ライブラリ(cloud.google.com/go/pubsub)の場合、環境変数PUBSUB_EMULATOR_HOSTに接続先を設
定する
● zendesk.com(APIが公開されている)
● Slack (github.com/nlopes/slack パッケージの場合、slack.APIURLに接続先を設定する)
● Google Drive (APIが公開されている)
Gatewayを経由したテスト
● 開発用GCP/GKE環境
で動作しているマイクロ
サービスをテスト
● HTTP POSTを使って
gateway経由でAPIをテ
ストする
● APIの正常使用、不正使
用、同時呼び出しなどの
テストを行う
テストファースト開発
● 新規機能開発
○ (「API仕様作成」→)「テスト作成」→「テスト不合格」→「実装」→「テスト合格」→「リファクタリン
グ」
○ 実際には、このサイクルをエラーケース、正常ケースと行う
● 機能修正
○ (「API仕様修正」→)「テスト作成あるいは修正」→「テスト不合格」→「実装」→「テスト合格」→
「リファクタリング」
● バグ修正
○ (「原因調査」→)「再現テスト作成」→「テスト不合格」→「実装」→「テスト合格」→「リファクタリン
グ」
初めてのウェブサービス開発で心得たこと
● API仕様をきちんと記述する
○ APIマイクロサービスも含めて、マイクロサービス間はgRPCで通信しており、その仕様をきちん
と.protoファイルに記述する
○ 詳しくは、「gRPCを用いたマイクロサービスのAPI仕様の記述」
(https://tech.mercari.com/entry/2019/05/31/040000)を参照
● 自分が作ったマイクロサービスのテストをきちんと行う
○ 特にフロント(iOS アプリやAndroidアプリ)と通信するAPIマイクロサービスは、フロントと接続するこ
となく、そのAPIをきちんとテストするTestSuiteを開発する
○ テストファースト開発を実践する
組み込みシステム開発 v.s. ウェブサービス開発
ウェブサービス
● 多くのプラットフォームが整備されている(GCP/GKE、Spinnaker、Docker、Circlci、etc)
● プラットフォームの使い方や設定を学ぶ必要がある(プラットフォームも発展している)
● 開発するサービスのロジックに専念できる
● ハードウェアやOSに関する知識がそれほど要求されない
● 複雑なマルチスレッドプログラミングはしない
組み込みシステム
● OSといった最低限の環境上に独自にソフトウェアを構築する
● ハードウェアやOSに関する知識が必須
● アーキテクチャによっては、複雑なマルチスレッド(or マルチタスク or ゴルーチン)プログラミングが要
求される
関連資料
● 「gRPCを用いたマイクロサービスのAPI仕様の記述」
(https://tech.mercari.com/entry/2019/05/31/040000)
● 『API設計の基礎』(http://www012.upp.so-net.ne.jp/eshibata/pdfs/apibasics.pdf)
● 「テスト駆动开発の経験」(ブログ記事)(https://yshibata.blog.ss-blog.jp/2019-01-23)
● 「テストファースト開発」(ブログ記事)(https://yshibata.blog.ss-blog.jp/2019-10-18)
● 「バグの修正の前に再現テストを先に書く」(ブログ記事)
(https://yshibata.blog.ss-blog.jp/2019-10-25)
● 「私が経験したソフトウェアテスト~ ワークステーション、組み込みシステム、ウェブサービス ~」
(JaSST ‘19 Tokai 特別講演)
(http://www.jasst.jp/symposium/jasst19tokai/pdf/S4.pdf)
ご清聴ありがとうございました
Thank you for your time and attention.
yoshiki.shibata@mercari.com
https://yshibata.blog.ss-blog.jp
https://twitter.com/yoshiki_shibata

More Related Content

GDG Dev Fest Tokyo 2019