
2025.02.18
「売上をスケールする」AIの使い道とは アルペンが挑む、kintone×生成AIの接客データ活用法
次世代タクシー配車サービス「MOV」におけるテスト事例紹介(全1記事)
リンクをコピー
記事をブックマーク
toku_bass氏(以下、toku_bass):「次世代タクシー配車サービス『MOV』におけるテスト事例紹介」というテーマで発表します。オートモーティブ事業本部、Twitterアカウントは@toku_bassです。よろしくお願いします。
(会場拍手)
社会人になってからやっていた言語は、C・Perl・Elixir・Goで、Goは1年ぐらいです。なので型のある言語は久しぶりです。ディー・エヌ・エーには入ってまだ1年経っていないです。
最初にMOVの紹介をさらっとして、どういう環境で開発をしているのか。あとは「テストを書く上でこういうことは気にしている」という方針の話と、並列テストをするために必要なテストデータの生成。残念ながらDIできないコードが存在するので、その打開の方法と、その他時間があれば発表します。
MOVの紹介です。ざっくりいうと、App StoreやGoogle Playストアでユーザアプリをダウンロードして、ユーザさんがすぐにタクシーを呼べるというサービスです。2018年4月に神奈川県限定でリリースされました。
僕が入社したのが2018年11月で、その1ヶ月後に東京でリリースされ、バンバンテレビで放送され、すごく負荷がきました。2019年7月に大阪と京都でもリリースされ、またテレビで放送され、またわりと負荷がきました。
配車アプリは「タクシー呼ぶだけかな?」という感じがあるんですけど、タクシーの中にも複数のAndroidの端末がありますし、あとは事業者様のところに管理画面があるので、それのためにやることがいっぱいあります。
あとはタクシーやユーザがどこにいるのかを管理しているサーバもありますし、いろいろなサーバがあります。今回の話は、サービス初期から存在する一般的なAPIサーバで使っているテストです。
環境としては、App EngineのGoの1st generationを使っていて、DBはCloud SQLの2nd generationを使っていて、あとはCloud Datastoreがあります。
テストの実行方法は、ローカルの手元のPCでgoapp testを実行する方法とGitHubのプッシュに連動してCircleCIで動くテストと、あとはchatbotE2Eです。E2Eといってもサーバで完結しているんですけど、配車系のテストをしています。
QAを依頼したものの、バグがあってQAが止まっちゃうことがあったので、そういうことをなくすために作られました。
テストを書く上での方針ですけど、今見ているテストケース以外に依存している暗黙のデータというものを用意してはダメで、テスト全体が始まる前にユーザを2人ぐらい追加しますみたいな、そういうfixtrueを使わない。あとはGoだとtesting.TのParallelがあって、単体テストが並列で回せるんですけど、これをちゃんと使っていく方針です。
テスト全体に関わるfixtureを使わないものです。マスターデータは別に使ってもいいです。マスターデータはゲームで言うところのアイテムの攻撃力や回復量などの企画で決まっているデータです。
マスターデータ以外の「ユーザを2人入れておきます」みたいなデータは、ユーザを1人追加するAPIがあってそれをテストして、「ユーザが3人になりました。OKです」というコードが書かれ始めて破滅します。
「そんなバカな」「ちょっと考えすぎだろ」みたいなことも思うかもしれませんけど、前職で10年物のすごいやつをメンテナンスしていてかなり大変だったので、本当に気を付けたほうがいいです。
並列でテストを実行するんですけど、APIのテストをDBまで共通してやろうと思うと、グローバルな状態を持っているDBがネックになってきます。なので、user_id=1みたいな固定値をテストでは書かれたくないと。今いったんは書かれているんですけど、ランダムにデータを生成するためのbxcodecさんのfakerというライブラリがあります。ざっくり言うとfactorybotみたいなものです。
このときはまだfakerに不満があって、いろいろ手を尽くすんですけど、数ヶ月前にv3が出てこれはけっこういい感じです。ユーザ定義のpluginも簡単に書けるのでこれがおすすめです。v2の頃からfaker以外のライブラリはあまり良いものがないと思っていたので、たぶんこれしかないんじゃないかなと思います。
実際にどうやって使うのかと言いますと、Userというstructにfakerというkeyでgotagがあるんですけど、emailというgotag valueを入れておくとemailの形式で適当なemailを生成してくれます。emailはfakerの持っているデフォルトのプラグインです。とくに何も単語を書かないとString型に合わせてそのデータを生成してくれます。
ここでemail以外にも作りたいとなってきたら自分で使うgatag valueを作れるので、これでごりごり書いていけます。
MOVでの利用例なんですけど、さきほど構造体のgotagにfakerと書くと言ったんですけど、チームメンバーから「プロダクトコードにテストのための記述は嫌だ」という意見が出て、そうだと思いまして。静的にコードを解析して関数名のprefixにfakeとついている構造体を生成して、それにgotagを付けました。
あと、プライマリキーはランダムに生成すると偶然被ることもあります。前職で大きいテストを回していたんですけど、そこそこ被ります。なのでtodoなんですけど、id採番のアルゴリズムを採用して原則的に被らないようにしていこうとしています。
次は「DIできないコードと戦う」。一般的にDIとよく言われるんですけど、Goではtime.Now()がどうしても撲滅することができなくて、絶対出てきます。
こいつをどうしたかというと、time.Nowの関数定義自体をどこかのpackage変数に保存しておいてラッパー関数を経由して呼び出します。それでテストのときだけpackage変数を書き換えられるsetterをビルドに含めます。この方法としてbuildタグを使っています。
ラッパーはどこのご家庭にもあるutilパッケージを作って……このnowFuncという変数にinit時にtime.Nowの関数を入れておいてプロダクションコードでutil.Now()と書いておくと、ふだんはtime.Nowが呼ばれるんだけど、テスト時はSetNowFuncという関数が使えるので、好きな固定値の時間を設定できます。
buildタグとはファイルの一番上の行にあるbuild testという記述の部分です。go testコマンドに--tagsオプションがあるのでそこにtestと書くとビルドの対象になります。
使っているところはこんな感じで、見ての通りでロックしているので、この手法を使うデメリットとしては並列で動かない点です。そこは甘んじて受け入れようかなと思っています。
tipsとして1つ言っておきたいのは、export_test.goを用意してsetterを作っていたブログを参考にして、util.Nowを作りました。だけど、うまく動かなくてすごいハマって「なんでだろう?」と思っていました。
utilに作っているので、例えばこのコントローラーのテストでutil.Nowを使いたい時にcontroller_test.goとかはビルド対象に含まれるんですけど、utilはテスト対象じゃないから、utilのディレクトリの下にexport_test.goというファイルを配置しても、それがビルドされないんですよね。なのでbuildタグで強制的にビルドに含めています。
あとは、これは完璧に力技なんですけど、Client実装をファイル単位で偽装します。これもbuildタグでやっていて、これはあまり説明したくないんですが(笑)。
シンプルなものだとこんな感じです。本来ならTagメソッドがオリジナルで呼ばれるんですが、package変数のmockTagが呼ばれるようになっていて、テスト時に、package変数に好きな関数を設定するやり方です。
これはかなりの業技なので、これを使わないといけない悲しみというのはあるんですが、「プロダクションコードでif(isTest)みたいなものは混ぜない」「リファクタリングをしないとテストが書けません」みたいなことは言わないという心意気はわかってほしいなと思います。
その他のTipsについて、よくあるのがtesteratorとpstestなので、その話をしようと思います。testeratorは有名なので知っている人もいるかもしれないんですけど、GAE用にテストサーバを上げると3秒ぐらいかかるんですよ。これをテストごとに上げ下げしていると、とてもじゃないけどやってられないので、ずっと上げっぱなしにするものです。
SpinUp関数を呼ぶと、すでにサーバが起動していたらそのコンテキストかサーバの情報を返してくれて、起動していなければ改めて起動をしてから情報を返してくれます。
検索すると、はてブとかQiitaが引っかかるんですけど、たまにテストサーバが落ちてまた起動に3秒かかるので「なんでだろうな?」と思ってソースコードの中を見てみると、内部カウンターを持っていて、SpinUpで1上がってSpinDownで-1になるんですけど、これが0になるとサーバが落ちるんですよ。
なので、テストの実装者が自分でSpinUpして行儀よくSpinDownを書くと落ちるんです。テストを始めるときに+1しておいてもいいんですけど、SpinDownが何故か2回書かれていることが往々にして世の中にはあるので、package全体で利用できるラッパー関数を用意しておいて、それで中央管理をしていきます。なのでSpinUpを直接呼ばないで「この関数を使ってね」としています。
最後です。Cloud PubSubのテストでpstestがすごい便利で、pstest.NewServer関数が擬似サーバを返してくれるんです。これにクライアントがリクエストを投げると結果が返ってきます。
これを見つけた経緯がエディターの定義ジャンプでPubSubのライブラリにとんだら、そこにテストファイルがあったので、「どうやってテストしているんだろう」と思って見たらpstestを使っていて、「あぁ、こういうふうに公式のライブラリを見に行くのはいいなぁ」と思って、この場で知見を共有しておこうと思いました。
ただ、BigQueryとかにはなかったです。公式のテストを見に行っても公式でも「TODO:このテストはまだできていません」とコメントが書かれている現実が8割ぐらいです。
以上です。ありがとうございます。
(会場拍手)
司会者:すみません。自分から質問です。pstestは公式パッケージ上の中に入っているパッケージみたいなやつですか?
toku_bass:そうですね。
司会者:ありがとうございます。もう1件質問が届いております。「idを採番するのにsnowflakeを使うのは?」という質問です。「他に例えばUUIDとかstandardぽいものがありますけど、そういったものはご検討されましたでしょうか?」
toku_bass:現状はUUIDじゃなくて数字で管理をしているので、snowflake的なやつがいいです。
司会者:はい。どうもありがとうございました。
2025.02.13
“最近の新人は報連相をしない”という、管理職の他責思考 部下に対する「NG指示」から見る、認識のズレを防ぐコツ
2025.02.13
AIを使いこなせない人が直面する本当の課題 元マッキンゼー・赤羽雄二氏が“英語の情報”を追い続ける理由
2025.02.06
すかいらーく創業者が、社長を辞めて75歳で再起業したわけ “あえて長居させるコーヒー店”の経営に込めるこだわり
2025.02.12
マネージャーは「プレイング3割」が適切 チームの業績を上げるためのマネジメントと業務の比率
2025.02.14
報連相ができない部下に対するコミュニケーションの取り方 「部下が悪い」で終わらせない、管理職のスキル向上のポイント
2025.02.13
上司からは丸投げ、部下からはハラスメント扱い、業務は増加…プレイングマネジャーを苦しめる「6つの圧力」とは
2025.02.12
何度言っても変わらない人への指示のポイント 相手が主体的に動き出す“お願い”の仕方
2025.02.13
「みんなで決めたから」を言い訳にして仲良しクラブで終わる組織 インパクトも多様性も両立させるソース原理
2025.02.10
32歳で「すかいらーく」を創業、75歳で「高倉町珈琲」で再起業 「失敗したからすかいらーくができた」横川竟氏流の経営哲学
2025.01.07
1月から始めたい「日記」を書く習慣 ビジネスパーソンにおすすめな3つの理由
着想から2か月でローンチ!爆速で新規事業を立ち上げる方法
2025.01.21 - 2025.01.21
新人の報連相スキルはマネージメントで引きあげろ!~管理職の「他責思考」を排除~
2025.01.29 - 2025.01.29
【手放すTALK LIVE#45】人と組織のポテンシャルが継承されるソース原理 ~人と組織のポテンシャルが花開く「ソース原理」とは~
2024.12.09 - 2024.12.09
『これで採用はうまくいく』著者が語る、今こそ採用担当に届けたい「口説く」力のすべて
2024.11.29 - 2024.11.29
第20回エクゼクティブメンターイベント「今、「ひと」と組織が共創する〜働き方の未来へ」
2024.12.07 - 2024.12.07