Seleniumを使ったシステムテストをAzure DevOpsに導入した話

この内容はAzure DevOps Advent Calendar 2020の1日目の記事です
qiita.com

Seleniumを使ったシステムテストをAzure DevOpsに導入した話

僕のホビープロジェクト、マンガログサービスマンガ読んだ!!は、フレームワークにBlazor、CI/CDにAzure DevOpsを使っています。作り出してから3年以上経ちますが、ずっとシステムテストを導入していなかったので、今回導入することにしました(この話はソフトウェアテスト Advent Calendar 2020にも書きます!)。

SeleniumをAzure DevOpsで動かす

BlazorはSPAのためシステムテストにはSeleniumを使いました。最初SeleinumをAzure DevOpsで動かすのは設定が色々大変かと思っていましたが、実はNuGetでSeleniumを指定さえしていれば、Azure DevOpsでは特に何も設定しなくても動きます。それよりもそもそもAzure DevOpsのPipelineでMsTestが動いてくれないことの方が悩みました(この解決はQittaにAzure DevOpsのPipelineのtaskでMSTestを動かす方法を書きました)。とりあえず設定出来てAzure DevOpsでSeleinumを使ったシステムテストは無事動くようになりました。

clickイベントに失敗する場合の対応

Azure DevOpsでテストが動くようになって、最初に躓いたのが、ローカルではオールグリーンになっているけれど、Azure DevOps上でのみ失敗になるケースです。最初は、clickイベントが全て失敗していました。これは、画面を最大化することで回避出来ました。RemoteWebDriverでウィンドウを最大化してやればOKです。

Driver.Manage().Window.Maximize();

タイムアウトの対応

次はAzure DevOps上のみで出るタイムアウトがありました。Seleniumタイムアウトはさまざまあるので、どこの設定かで悩みがちです。今知っているのは5個のタイムアウトの設定があります。今回僕がタイムアウトとして解決するにはChromeDriverの第三引数で指定するタイムアウトでした。最初はPageLoadのタイムアウトだと思っていたので、あれ?変わらないと悩みました。それぞれマニュアルを読んで正しいタイムアウト値を設定するようにしましょう。

Driver = new ChromeDriver(ChromeDriverService.CreateDefaultService(), new ChromeOptions(), TimeSpan.FromSeconds(120));
Driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(120);
Driver.Manage().Timeouts().PageLoad = TimeSpan.FromMinutes(10);
Driver.Manage().Timeouts().AsynchronousJavaScript = TimeSpan.FromMinutes(10);
DriverWait = new WebDriverWait(Driver, TimeSpan.FromSeconds(60));

不明なエラー対応に画面キャプチャ

そして、これ以外にも、Azure DevOps上のみで出る理由が分からないエラーがありました。Azure DevOpsは実際Seleinumで実行しているUIを見ることが出来ないのでエラーの特定が難しくなります。せめてエラーが出た瞬間の画面キャプチャが見られれば大分楽になるので、それが出来る仕組みが欲しくなりました。(これもQittaにAzure DevOpsでSeleniumを使ったシステムテストがエラーの場合に画面キャプチャを見る方法を書きました)。画面キャプチャなくエラー対応するのは結構厳しいと思います。

まとめ

Seleniumを使ったシステムテストをAzure DevOpsに導入するには、実はAzure DevOpsとしては殆どやることはなく、むしろMsTestでの設定をやっておく必要があります。ただ、ローカルでは起きなくても、Azure DevOpsで動かしたことで起きる問題はままあるので、その対応がむしろ大変かなと思います。その際にはエラー時に画面キャプチャがあると大分違うので是非設定しておくことをオススメします。

マンガ読んだ!!にシステムテストを導入したくて、モブプロでアドバイスを頼んだ話の第3回

システムテストを導入したい。前回の宿題

マンガ読んだ!!というサービスを作っています。第2回に続き、モブレビューしながらプログラミングの第3回です。先ずは前回の宿題を調べた結果の共有。Blazorがテスト用のパラメータみたいなものを持っているものは見つけられませんでした。最近Blazor向けのbUnitが出ていましたが、こちらでもDOMの操作はFindでタグを指定しています。別にこれが証拠になるわけではないですが、まあ現状は普通にDOMで書かれてものを操作するしかないかなという結果です。

モブプロ前の雑談での発見とか

始まる前に大抵雑談してますが、今日はシステムテストとは関係ないけれど、今作っているサービスで有益なことを2つ教えて貰いました。と言っても結構基本的なことです。先ずクッキーの話になって、保存するストレージとしてクッキーが最適か考えた方が良いと言われました。僕はこのサービスが実質初のWebアプリに近いので、結構常識的なことも抜けていたりします。マンガ読んだ!!を作り始めた3年前は特に。その時からクッキーを使っていたので、それ以上考えたことがなかったです。クッキーはストレージとしては最も長く最も広くアクセスできます。本当にそこに保存するべきなのか。他にもローカルストレージやセッションストレージもある。それぞれの保存期間とアクセス範囲を考えると、今の使用用途だとクッキーは最適ではないとなりました。セッションストレージだと短すぎるのでローカルストレージを使うべきだと分かりました。もう一つはこのモブミーティングを始める前にslackにエラー通知が流れて来た話になって、原因が最近入れたTransactionScopeにありそうと言ったら、実行順番をちゃんと意識して作っている?と言われて、あんまり意識していなかったので、そこを見直してみることにしました。これで問題解決になったわけではないですが、これがきっかけで見直して解決したので、ありがたやーちゅう感じです。雑談中に発見があるのもモブプロのメリットですよね。まあ単なる雑談も(かなり)してますけれど。

振り返り

さて、まずは振り返りを兼ねて、Titleをいつ切り替えるべきかで自分の中で疑問が生じたので聞いてみました。そもそも現状遷移用のIDを作って、そっちはDOMが書き終わったタイミングで書き換える。一方タイトルはBlazorの初期化処理の中で書き換える。だったらまとめちゃったら良いんじゃない?と思っていました。まずそもそもいつタイトルを書き換えたいの?という話になりました。モブプロやっていると、結構、このそもそもやりたいことの話になります。つまり話を始める時、自分の中で結論が出ていると結果依りの所から会話を初めてしまいがちですが、相手にとっては前提がないので、これは非常に重要だなと気づかされます。特に自分は結論よりから話す傾向が強いように思えたので、反省しています。で、タイトルを書き換えたいベスト。んー正直あんまりない。タイトルがいつ書き換わるかは重要視していなかった。あえて言うならすぐか、ページが完全に書かれた後かな。ただ、SPAで完全に書かれた後は制御しづらいので、タイトルは一番に書き換えるのが良いとしました。ついでにたまたま最新版を調べたらBlazorの最新版を使えばタイトルの書き換えはjsを使わなくても出来そうな話もしました。

コピペの罪悪感

で、ちょっとずつテストを書くのですが、一旦テストを全部そろえる方針にしたので、前回作ったメソッドをコピーして、今回のメソッド用に一部書き換えるということを繰り返しました。これはねー。正直普段だとちょっとあり得ないやり方で、コピペの罪悪感が結構溜まっていきました。ダーティでも書ききるという方針を作ったので、みんなもこれに関しては言いたいけれど言えないそんな状態。で、僕が、「やっぱりコピペプログラミングは罪悪感が凄い」みたいなことを言ったら、「あ、それなら逆に安心」となりました。「これを許容してやるようではやっぱり厳しいよね」と。今回テスト数を絞ったので、まだましですが、もっとテスト数が多かったらフラストレーション爆発が起きていたかもしれないです。

state管理を数字から名前に

ページが少し揃ってきたので、そろそろstateを数字で制御するのが無理になってきました。最初のページが0で次のページに行ったら1にして、もう1個移動したら2にする。数字で制御できるのは小学生までということですか。で、文字にすることにしました。文字つまり名前になるなら何にするべきか。これはページのファイル名にしました。これであればユニークなのは保証されているし、考える手間も省けます。これでタイトルからそれぞれ、マンガ、シリーズ、作者に移動するフローは出来ました。次は詳細ページです。詳細ページの場合タイトルは固定ではなく、各マンガのタイトルになります。つまり動的に変わります。これはurlがタイトルを持っているので、それと比較することにしました。あとはこれを各ページに充てて量産すれば8個中7個のテストは完成です。

謎バグ

さて終盤謎のエラーに悩まされました。結論から言うとこのミーティングが2週間起きなので、マージをしていないことが大きな原因でした。まあ正直作りも悪かったですが、そもそも間隔が空いたのならマージをしないとは基本ですが、やっている時は見落としていました。。。ただ、それをもってしてもjsファイルが更新されていないのはちょっと分からなかったです。

マンガ読んだ!!にシステムテストを導入したくて、モブプロでアドバイスを頼んだ話の第2回

システムテストを導入したい。前回の宿題

マンガ読んだ!!というサービスを作っています。第1回に続き、モブレビューしながらプログラミングの第2回です。先ずは前回の宿題を調べた結果の共有。1つはBlazorでSeleiniumを作った時にExpectedConditionsが出る件。これはstackoverflowとかを見てもわりとこの方法でした。また例外で出るNoSuchElementExceptionを自分で握り潰している方法もありました。ExpectedConditionsのままだと警告が出るため、これは一枚ラップして、いずれ例外を握りつぶす方に変えるつもりだけど一旦は現状維持にしました。もう一つの開発環境の方はVSを「デバッグなしで開始」すればその間はサーバーが立ち上がっていて、かつこの状態でテストを書いてもロックはされなかったので、この方法で進めることとしました。ただ、でも何でロックしないんだろう?という話になって、考えてみるとロックされない理由は説明できなかったので少し謎が残ってしまいました。

Azure DevOpsの設定

あと宿題にはなっていなかったですが、Azure DevOpsでの設定も確認しておきました。Seleinumの設定など特に何もしなくてもNugetで入れておけば、それだけで勝手に動くという目論見でした。ただ実際に、DevOpsで設定すると他の部分で大苦戦しました。。というのも、大分前に設定した時、まだ.net coreではなくて、yamlでもない時は特に悩んだ覚えがなかったので、今回もサクッと行けるだろうと思っていたのです。ところがVSTestでテストの設定をしてもエラーが続く続く。色々調べて回避方法を見つけても実際に試してみてもエラー。何十回も設定を変えても結局上手くいきません。結論としてはVS Testを使わず、DotNetCoreCLIのTestを使えば、デフォルトのままでちゃんとテストが動き、かつSeleniumも問題なく動きました。何故VSTestが動かないかは分かっていません。

テキストエディタトーク

で、その後、どんな感じでテストを作っていくかの話になって、マンガ読んだ!!はベースとなるページはわりと少なくて、スタートページ、マンガ、作者、シリーズの個別と詳細、後はログインした後の読んだ一覧画面の8画面がテスト出来れば十分ではないかとなりました。マンガの詳細画面はマンガによって違うので実質20万ページ以上ありますが、それは全ページやっても意味が薄いだろうと話しました。ここまで何となくテキストエディタを起動して書きながら話をしていました。書いているテキストは文章としてのあまり意味はなしていない、後から見直しても意味は分からないような走り書きですが、単語単語は意味があるものです。例えばマンガ、作者、シリーズのように単語が書いてあって走り書きしているような状態です。じゃあ、今書いたものを少し整理して、あとはそれを日本語のテスト名にすれば完成だと言われました。ほぉ。なるほど。そういうアプローチがあるのかと感心してしまいました。

人間の動きに合わせたシステムテスト

で、実際に「TOP→詳細」というメモから「トップ画面から移動してシリーズ画面を表示」というテストメソッドを作りました。この名前になるまでもモブでちゃちゃが入ります。例えば、「「表示する」と「表示」とどっちが良い」?とか、「表示は自明だから必要かな」とか。こういうのはなかなか自分だと思いつかないものもあり結構楽しいです。で、一応8個のテストメソッドを作りました。前回はトップ画面までの移動だったので、今回はここからシリーズ画面への移動を作りました。その際は、人間が移動する、つまりは僕自身が普段どうやってその移動をしているかをテストとして書くべきと言われました。なんとなく分かってはいてけれど、明確に言葉となって進行がとてもクリアになりました。現状移動に使っているボタンには一意となるclassはありますが、意味がある名前が付いていなかったので、ここも新たにidを定義しました。

ダーティでも書ききる方針

これで前回作ったwaitというidのdate-stateの番号を変えていけばその都度待ったことに出来ます。stateを0や1はどうかなと思ったのですが、一旦コードのリファクタリングとかは置いておいて、ダーティでも良いのでさっき作った8個のテストをそろえることを目標としました。この辺も定義が明確な方が進めやすいモブならではかもしれません。この方針にしたので、パッと思いつかなかった時に超久しぶりにSetState2というクソみたいなメソッドも作ってしまいましたw。ただページの切り替わりに関してのアプローチは、フレームワークで変更をとれるパラメータのようなものが用意されていればそれが一番良いだろうとなりました。Reactとかにはフレームワークにそういうものがあるという話になったので、Blazorにもそういうものがあるか調べるのは宿題となりました。

まとめ

で、「トップ画面から移動してシリーズ画面を表示」と「トップ画面からシリーズ画面に移動してシリーズ詳細画面を表示」するメソッドまで作ったところで、他のページもと思っていたら、結構いい時間になったので今回はここまでとしました。白状すると、開始までにプログラム以外での雑談に結構時間を使ったりしていますw。と言うわけでまた2週後に第3回をやります。

マンガ読んだ!!にシステムテストを導入したくて、モブプロでアドバイスを頼んだ話

マンガ読んだ!!にシステムテストを導入したい

マンガ読んだ!!というサービスを作っています。マンガ読んだ!!は残念ながら現在殆どテストを書いていません。そのため以前から思いもよらないページが落ちていることが何度もありました。だからシステムテストを導入したいという気持ちはありました。Webのシステムテストに関して、Seleniumを使ったテストを書けば良いことは分かっていましたが、マンガ読んだ!!はSPAなのでテストの書き方にもコツがいりそうです。また僕個人がWebの実務経験がないので、htmlのタグの勘所も良くわかっていません。他人のアドバイスを借りないとスタートでかなり時間を取られそうと思ったためアドバイスをもらうことにしました。今回は実験的にモブプロ形式でお願いしました。メンバは、僕にとっては定番である kazuhito_mさん,Hidari0415さん,USHITO Hiroyuki / kesさん。ありがとうございます。

Visual Studio Live Shareの実験→zoomのボイスチャットに変更

マンガ読んだ!!は全てVisual Studio(以下VS)で開発しています。モブプロするなら Visual Studio Live Share(以下VSLS)がベストかなと思いつつ一度も試したことがなかったので、今回はこの実験も兼ねました。他の人がVisual Studio Code(以下VSC)だったので、Vs vs VSCで試してみました。音声チャットも出来そうなのは以前見た記憶があったのでとりあえず他のツールの検証なしにいきなりVSLSを使ってみました。音声に関してはVSCは使えてもVSが非対応だったので、zoomのボイスチャットを併用しての実験になりました。結論から言うとVs vs VSCでのVSLSはうまくいかなかった感じです。何度か編集した所が何個も増幅した感じになったりして、同期自体がおかしいのか良くわからない状況になり、最終的には自分のVS以外は全て接続を切って、zoomのボイスチャットのみで進行する形になりました。ちと残念。

自分が書いていたSPA用のテスト

今回準備に自分が作っていたのはSeleniumを起動させて、起動した画面のタイトルが正しいかのテストのみです。ただBlazorでもindex.htmlだけは静的に決まっているのでそのタイトルが正しいのかのテストでしかなくSPAを全く考慮していません。SPA的に画面が切り替わったことをチェックするテストを書こうとして色々悩んだと話しました。で、それを言ったら、まず待つものをどこにどうやって定義するべきかを決める所からという話になりました。自分的には各ページ毎にclassで判定用タグを仕込んでいく形になるかなと思っていました。最初にDOMを書き終わったというイベントが取れるかという話になりました。Blazorにはライフサイクルイベントメソッドがあって、OnAfterRenderAsyncがあります。ならフラグはこのタイミングで書き換えるべきとなりました。次にそれを書くべき場所は何処だろうとなりました。body要素の中が良いだろうとなって、ここでさらに僕の疑問を聞きました。

タグで使うべきはclassかidか

それはidとclassのどちらを使うべきかです。これに関してはみうみう氏に明確な答えをもらいました。対象が一位になるならid、そうでなければclass。で、今回作るものは誰が使うか?テストが使う。逆に言えばテスト以外は使わない。つまりid一択で良い。なるほど!と思いました。例えばデザインで使うのであればそれを他にも充てたくなるのでidになることは少ないことも分かりました。明確な定義が分かってスッキリしました。ロジカルに考えば当然行きつく考え方かもしれませんが、それを思いつかなかった僕としてはこの話が聞けただけで、今回やった価値がありました。ありがとうございます。次にidのvalueをどうするかとなって、data-stateを進められました。これがあるとjsでdataset.stateを使えば簡単に書き換えられる。なるほど知らなかったのでこれは便利だなと思いました。ただ、ここでBlazor固有の問題がありました。

Blazorでの問題

Blazorだと共通のBodyを動的に書き替えるのがそれほど簡単ではないのです。各ページから共通要素への書き換えは出来ないわけではないけれど面倒です。jsならdocument.getElementById('wait').dataset.state = 1;で済むのに。と言うわけでここはBlazorからjsを呼ぶことにしました。これでページが切り替わったときに、IDが1になるのを待てば、SPAでページが切り替わったことがテストできるはずです!ところが実際に書いてみるとWebDriverWaitの待ち受けが上手くいきません。NoSuchElementExceptionで落ちてしまいます。wait.IgnoreExceptionTypes(typeof(NoSuchElementException));を書いても駄目。これは全員はまってしまいました。ここでUSHITO氏がExpectedConditions使ってみたらという話になって、使ってみると確かに例外が出ませんでした。ただExpectedConditionsは警告が出るぐらい古い書き方。んー。一旦これで行くことにしつつもっといい書き方がないかは宿題となりました。これで一応画面の切り替えを待ってタイトルのテストを書くことが出来ました。この時点で2時間程度たってしまったので、終わりにしました。

開発環境の宿題

それともう1個宿題。環境面の問題として、Visual Studioでテストを書くにあたって、ローカルでテストをしようと思ったらローカルでサーバーを立ち上げる必要があります。開発中はBlazorのServerでデバッグ起動すれば勝手にサーバーが立ち上がって、ブラウザを終了すれば勝手に閉じてくれます。自分はローカル開発ではこれ以外でやったことがなかったので、テストの時ローカルでサーバーを立ち上げておく方法が分かりませんでした。なのでVSをもう1個起動して、それをデバッグ実行して、テストするようにしました。ところがこの方法だと、クライアント側も書き換えたい時にファイルがロックされてビルドが通らない状態でした。なのでサーバー側を閉じて、書き換えをして、終わったらまたサーバーを起動してというかなり面倒な状態になりました。この状態で今後も続けるのはちょっとあり得ないなとなったので、解決策を次回までに探っておくことになりました。

まとめ

結局今回はBlazorでシステムテスト1個がなんとなく動く状態になりました。画面の切り替えはidが定義されていてそのstateが変化したことで待ちが出来ます。1個できればあとは量産ですが、まだそこまで練られた感じでもありません。コードだけでなく、開発環境としても現段階では無駄が多いので、次回までに宿題を解決して、量産出来る状態になればと思いました。当初予定したモブプロにはならなかったけれど、思考を言葉にして、それをディスカッションしながらコードを書いていく体験は、気づきも多く、またなにより楽しかったです!これがモブプロなのかは定義が分かってないのですが、自分的には名前を付けるならモブレビューしながらプログラミングかなと思いました。と言うわけで2週後に第2回をやります。

「マンガ読んだ!!」のAzure Searchを使ったテキスト検索について

この内容は Azure Advent Calendar 2019 - Qiitaの22日目の記事です。僕はマンガ読んだ!!いうサービスを1人で2年半以上作っています。マンガ読んだ!!は、自分が読んだマンガのログを残すことが出来るマンガログサービスです。

マンガ読んだの検索テキストボックス

マンガ読んだ!!ではデータベースにSQL Serverを使っています。さて、マンガ読んだには検索用のテキストボックスがあります。普通、検索テキストボックスは全文一致を採用していると思います。つまり「こちら」と打っても、「葛飾区」と打っても、「派出所」と打っても、「こちら葛飾区亀有公園前派出所」がヒットします。クエリ的に言うと、Title like N'%*%' です。そして全文検索はとても遅いです。実際マンガ読んだ!!での検索テキストボックスの検索は作った当初遅すぎました。

マンガ読んだの検索テキストボックスの検索が遅かった理由

いくつか原因が重なってなんと30秒近くかかっていました。最初は完全一致で作っていて、ある時全文一致に変えたので、気づいていなかったのもあります。完全一致なら5秒ぐらいでした。ただ、その他にもBlazorの技術的な理解不足により検索が2回以上走っていたことやクエリ自体の書き方にも遅くなる問題がありました。しかし、それらを解消してもまだ使えないぐらいの遅さでした。一番は単純に全文一致が遅いんです。

Azure Searchの高速性

当初知識がなかったですが、全文検索はやはり何かしら別のアプローチをするのが常套手段のようでした。その時調べていて候補に挙がったのがAzure Searchでした。Azureのサービスの中に解決法があるとは思ってなかったので、これを見つけた時は、お、使っているサービスで賄いきれるのかとちょっと嬉しくなりました。で、使ってみました。Azure SearchはSQL Serverを元にインデックスを作る機能もあるので、あまり何もみずポータル上でポチポチやっているだけでもわりと使えました。そしてポータル上で全文検索出来たので使ってみると…、え!一瞬じゃん。。1秒以内にレスポンスがあって、本当に驚きでした。Azure Searchを使う意味はもうここがほぼ全てと言っても過言ではありません。トニカク速い!

Azure Searchヒットの謎

さて超高速で使える全文検索ですが、ヒットする内容を詳しく見ていると多少の謎があります。例えばHUNTER×HUNTER 1で検索した場合、HUNTER×HUNTER 1が最も高得点にヒットします。これは分かりやすい。ただ2番目以降のスコアは良くわかりません。特に?と思ったのが「CITY HUNTER」が「HUNTER HUNTER総集編」より高いスコアでヒットしたりしました。また、点数が低いとなんでこの内容がヒットした?と思えるものもあります。あいまい検索でのヒットも同様で、どの曖昧語でヒットするかが良くわかりません。HANTERはヒットしませんでした。ただ、ここを突きつけめても結局UI/UXの向上には殆ど繋がらないと思ったので特に掘っていません。

Azure Searchプログラムインデックス

さて、ポータルポチポチで作れても、ちゃんと使っていくにはプログラムでの操作も必要です。最初にインデックスの更新です。一応Azure Searchに自動同期機能もあります。スケジュール設定で例えば1日1回同期させておけばSQL Serverが変更していても連動します。これでよければやる必要はありません。ただ、やはりSQL Serverへ追加すると「同時」にAzure Searchも更新したくなります。そうなってくるとプログラムで更新してやる必要があります。SQL Serverのテーブルと同じclassを使いまわしてやればすむ…と思ったのですが、出来ない。何故だ何故だと思ったら罠がありました。SQL ServerのテーブルのIDはint、Azure SearchのテーブルはIDがstringです。なんで、stringにした??

Azure Searchプログラム検索

次にプログラムで検索しようとすると、クエリ構文がちょっと面食らいました。特に比較演算子記号(=,<,>)じゃないので都度調べるのが面倒です。普通のクエリにしてくれれば良いのにとは思います。検索がプログラムで出来るようになって、フィルターやソートに対応出来れば検索としてはほぼ全て出来ると思います。

インクリメンタルサーチ

あとわりと大事な機能にインクリメンタルサーチがあります。Azure Searchはこちらにも対応していて結果を返してくれます。インクリメンタルサーチhtml5のdatalist タグを使うとかなり楽に実装出来ます。ところがdatalist タグ自体にも問題が結構あるみたいで、特に全角英語が混じっていると上手く出ないことがあったりします。でも100%じゃなくて良くわからないです。あとAzure Searchが出す一覧も同じように謎があります。でも、まあそれらの問題がありつつも、使えなくもないレベルで使えます。逆にその手の問題がコントロール出来ても抜群に使いやすくなるとも思えず、スルーしてます。

まとめ

Azure Searchは兎に角速いのでこれだけで使う意味があります。いくつか分からないこともありますが、分からないなりに使ってもそれなりに使えます!

「マンガ読んだ!!」のCI/CDについて

この内容は CI/CD Advent Calendar 2019 - Qiitaの15日目の記事です。僕はマンガ読んだ!!いうサービスを1人で2年半以上作っています(すいません。Azureの課金の問題で最悪今月はサイトに入れないかもしれません)。マンガ読んだ!!は、自分が読んだマンガのログを残すことが出来るマンガログサービスです。

マンガ読んだのバージョン管理とCI/CDについて

マンガ読んだ!!は開発はBlazor、サーバーはAzureでApp Service、バージョン管理はgit、リポジトリはAzure DevOpsを使っています。gitとは言え、ブランチすらも碌に切っていませんでした。理由は、言ってしまえばそれで成り立っていたからです。一人なので当たり前ですが、コンフリクトも殆どしたこともありません。デプロイはVisual Studioから行っています。GUIで選んでボタンを押すだけなので、手動デプロイと言っても簡単なもんです。結論から言うと、一人開発でいえば割り切ってこの運用でも、正直問題ないんじゃないかなと思います。

git flowをどれにするか

ただ、自分自身CI/CDに関しては一過言ある方で、流石にCI/CDもせず、ひたすらmasterブランチ一本というのもどうかと思って、色々設定することに決めました。まずflow戦略から考えました。すなわち、git flowかgithub flowかgitlab flowかです。先ず、個人開発においてgit flowは複雑過ぎるし、手間が増えるだけで、メリットを感じません。選ぶならgitlab flowにするか、github flowにするかです。とりあえずそれぞれのフローを把握した状態ではgitlab flowか一番良さげかなと思っていました。masterにマージしてもデプロイしたくないタイミングもあるだろうし、tagでproductionへマージすればログとしても扱いやすいし。

トリガーとマージ

で、実際Azure DevOpsを設定しながら考えました。まず自動デプロイのタイミングです。Azure DevOpsはgitでmasterへのマージをトリガーに出来ます。でも、masterにマージされるタイミングでチェック走らせたら遅いよなと思いました。だってマージされたときにテスト失敗したらそれはマージする意味がないです。じゃあどうするかというプルリク時にもトリガーが設定できます。プルリク時にCI走らせて、ビルドエラーやテストエラーだったら、そのプルリクは完了しない。つまり、masterにマージ出来ない。これは良い感じです。でもこのタイミングつまりプルリクで完了してない状態でCDされたらそれも困ります。つまりCIとCDでキックして動かしたいタイミングは違います。CIはプルリク開始時が良くて、CDはgithub flowならmasterへのマージ時、gitlab flowならproductionへのマージ時が良いです。

実際の運用

で、実際設定して、さらにプルリク時には、ステージングへのデプロイをやるようにしました。つまりプルリクをした段階でCIが動き出し、このプルリクを完了しようが跳ねられようが、ステージングへのデプロイまで完了します。これで、実運用としては、フューチャーブランチを切って、タスクが実装出来たら、プルリクをする。で、しばらく待っているとCIが完了してステージングへデプロイされます。ローカルチェックで不安の時は、ステージングで状態確認すれば安心感が得られるし、システムテストも可能です。問題ないと思っている時はそのままプルリク承認すれば、それでmasterへマージされます。一旦この状態でmasterへのマージ時に本番へのデプロイもやるようにしました。つまりgithubflowを採用しました。

で、github flow

で、github flowで開発を続けていたらgitlab flowにする理由がなくなってしまいました。github flowで困ることが特になかったんです。今後もずっとこの運用で行くかは分からないですが、個人開発だったらこれで十分かなと思います。正直ブランチを切るのも煩わしい時はまーまーあります。最初にも書いた通り、何もしないのも一つの手です。でもやっぱりCI/CDしていると楽できることもあります。これは運用やチームによっても大きく変わると思いますが、自分にとってgithub flowが一番良い温度感でした。

僕が「マンガ読んだ!!」でBlazorを使い始めた理由

この内容は Blazor Advent Calendar 2019 - Qiitaの8日目の記事です。僕はマンガ読んだ!!いうサービスをBlazorで1年以上(サービス自体は2年半以上)作り続けています。マンガ読んだ!!は、自分が読んだマンガのログを残すことが出来るマンガログサービスです。というわけで、今日はBlazorをサービスに使い始めたきっかけを書きます。技術的な内容ではなく、エモい方です。

C++ Builder

その前に少し昔話を書きます。プログラムを初めて、開発環境に対してのこだわりは殆どない方でした。与えられた開発環境をそのまま使う。それだけでした。ある時Windowsのツールを作ることになってC++ Builderというソフトを渡されました。ハマリました。ツールを作るのにこんな素晴らしい環境があるのかととても驚きました。あまりに気に入りすぎて、まだ社会人1年目だったにも関わらず、結構高いのにソフト買ったぐらいです。それからもツールを作るときはC++ Builderでした。

Visual Studio & C#

それから何年かして、ある新しい開発環境に出会います。それがVisual Studioでした。C#という聞いたことのない言語と共に。その時C++しか知らなかった僕ですが、触ってみて本当にどハマリしました。これはC++ Builderより良いかもしれない。しかも信じられないことに無料で使える。その後Visual StudioC#は僕にとって最強の開発環境になります。そしてその後この2つの開発環境を作った人は同じ人だと知ります。その人こそ、僕の神アンダースヘルスバーグです。まあ、何が言いたいかというと、自分にとってVisual StudioC#は、神からの贈り物ということです。

Vus.js & TypeScript

で、自作サービスの話に戻ります。ASP.NET MVCで1年かけて作ったサイトを公開して、そこから大幅な技術見直しをして、Vue.jsでの開発に移行しました。Vue.jsの開発を始めた時、Blazorは一応知っていました。けれど、飛びつきませんでした。その時はまだexperiment(実験的)というタイミングでしたし、そんな時期に手を出しても茨だろうなと。でも、正直Vue.jsの開発がどうにも楽しめませんでした。さまざまな問題があってVisual Stuidoのポテンシャルは行かせないし、サーバーがC#でクライアントがJavascriptという二刀流は結構なストレスがありました。何かあったときにコンテキストスイッチの切り替えはやっぱり嫌です。TypeScript(余談ですがTypeScriptの開発者もアンダースヘルスバーグ!)という選択肢もあったんですが、個人的な感覚ではVue.jsとTypeScriptの愛称も決して良いと感じませんでした。僕がやった時期に丁度TypeScriptとの連携が強化されたんですが、TypeScript的に書けるようになったというより、TypeScriptをVue.jsぽく書けるようになったという印象でむしろマイナスと思っていました。

Blazorを使い始めたきっかけ

で、何ヶ月かVue.jsでの開発を続けて、フラストレーションが大分溜まっていました。そんなときに僕は捻挫しました。痛くて家プログラムが1週間ぐらい組めませんでした。そして大分マシになったので、プログラムを再開するとなったときに思いました。よし Blazorに移行だ!と。つまり、なんらロジカルな思考で選んだのではありません。捻挫して衝動的に乗り換えただけです。でも、いざ使ってみるとめちゃめちゃハマりました。数か月やっていたJavaScriptにもっている不満みたいなものが、Blazorにしたことで全て吹っ飛びました。良くわからない設定ファイルを書かなくて良いし、ああ書きたい、こう書きたいと思っていた書き方がサクサク出来る。何より2つの言語を使わなくて良いのがとても快適でした。やっぱり多くの人は慣れ親しんだ言語で書きたいと思います。Rubyが好きな人ならサーバーサイドはRuby on Rails、クライアントサイドはJavaScriptで書くよりやっぱりRubyで書きたいと思います。BlazorはクライアントもC#で書けるこれが最大のメリットです。

Blazorをオススメする人

そして技術的なメリット以外にもまるで故郷に帰ってきたような圧倒的な安心感と何より作っている楽しさが全然違いました。書いていて楽しい、これが最大のモチベーションです。自分には楽しくなかったVue.jsの開発では下手したら折角1年以上も開発を続けていた自作サービスを途中で止めてしまったかもしれません。Blazorを使っているからこそ、1年以上の開発を続けているとも言えます。だから、Blazorを使うのに適している人は「Visual StudioC#でプログラムを組むことが楽しいと思っている人」です。そういう人でSPAの開発が選択肢に上がるのであれば、もう圧倒的にオススメします。Blazor開発とっても楽しいですよ!!

明日

明日の Blazor Advent Calendar 2019 - Qiitaの9日目の記事はもう少し技術的な話題なのでブログではなくQittaに書くつもりです。
http://manga-yonda.com/manga-yonda.com