Life is Really Short, Have Your Life!!

ござ先輩の主に技術的なメモ

Terraformをやってみた

AWSを触るのは2年ぶりぐらいかな…

インフラはVPSに逃げがちでしたが、VPSで頑張ってもなんの成長にもならんので、AWSでインフラをコード化することにトライした。CloudFormationは、色々とググったYAMLを見た時にこれをちまちま書くのは無理!と思ったのでスルー。Terraformはシンプルに設定を書き連ねることができるフォーマットなので、流れが追いやすかった。

1日ちょいで、VPC作成からIGWアタッチしてサブネットとルーティング設定、ALBに証明書と独自ドメイン当てて、FargateでnginxをリバースプロキシにしてWSGIアプリを動かすところまできた。結構なタスクになるなぁ。

  1. VPC作成
  2. IGW作成→VPCアタッチ
  3. サブネット作成
    1. パブリック2つ
    2. プライベート2つ
  4. ルートテーブル作成
  5. ルーティング→関連付け
  6. セキュリティグループ作成
    1. HTTP(80)
    2. HTTPS(443)
  7. ALB
    1. ALB作成
    2. リスナー作成(HTTP, HTTPS)
    3. ターゲットグループ作成
    4. リスナールール作成(HTTP→HTTPSのリダイレクト等)
  8. ECR
    1. レポジトリ作成
    2. イメージをPUSH
  9. ECS
    1. クラスタ作成
    2. セキュリティグループ作成
    3. ECS用IAMユーザー作成
  10. Route53
    1. ドメイン登録
    2. ホストゾーン登録
    3. ACMで証明書発行 & 検証用のDNSレコード設置
    4. ALBに証明書をセット
  11. S3
    1. ALB ログバケット作成
  12. Cloudwatch
    1. CloudWatch用グループ作成
  13. Aurora
    1. セキュリティグループ作成(3306)
    2. クラスター作成(サブネットは既に作成済)
    3. インスタンス作成

初めてAWS触ったの、確か2011年ぐらいだけど、10年近く前からVPCあたりのネットワークの仕組み、全然変わってない。各々のサービスを使っていい感じにやれる幅は広がっているけど。

ECSのサービス作成並びにタスク設定は、ecs-cliでやります。docker-compose.yamlとecs-param.yamlのほうがとても直感的に設定ができる。

Flutterのテストは、ウィジェットレベルだけでよくね

Flutterには3段階のテストパターンが存在する。

  • Unit Test
  • Widget Test
  • Integration Test

アプリケーションの性質にもよるだろうけど、ローカル or HTTPでデータをフェッチして表示するというロジックが多くを占める場合、Unit Testでテストすることがかなり少ない。Model側にビジネスロジックがある場合(数の集計)ぐらいかなぁ。なので、UnitTestを書くシーンがあまりない。

購買するようなアプリケーションの場合、ユーザー目線だと、以下の手順を踏む。

  1. 商品を一覧から選ぶ(画面A)
  2. 数量を決めて追加を押す(画面B)
  3. お買い物かごに表示される(画面C)

WidgetTestになると、こういう感じかなぁ。

  • 画面A
    • 商品の一覧がリスト表示され、意図した箇所に情報が出ていること
    • スクロールしたらアイテムが追加されること
    • pull to refreshができること
    • タップしたらB画面に遷移する
  • 画面B
    • 必要な情報が意図したところに出ていること
    • 数量が増えたり減ったりすること
    • pull to refreshができること
    • タップしたらC画面に遷移する
  • 画面C
    • 必要な情報が意図したところに出ていること
    • アイテムの増減に伴い合計額などが変動すること

ウィジェットの動作が担保できているなら、頑張ってインテグレーション・テストを書くコストを取るべきなのか。それとも、インテグレーション・テストを頑張って書くのが良いのか。

・・・って思ったけど、ここで重要なのはE2Eの担保じゃなくて、ウィジェットの動きの担保だから、ウィジェット・テストで充分な気がするな。

Flutter(iOS)でアプリがアンインストールされたかを知る

キーチェーンに書き込んだ情報はアプリがアンインストールされても残ってしまうので、ユーザーがアプリを再インストールした時にそのtokenが残ってちょっとやだ、みたいなケース。

結論から言うと、NSUserDefaultsに書き込んで逃げるしか無いっぽい。アプリがアンインストールされたら消えるローカル情報、ここしか無い。

stackoverflow.com

Flutterだとこれ。やってること一緒。

stackoverflow.com

Flutterで任意の画面までPopUntilしたい

  1. 商品検索
  2. 商品詳細
  3. 会員登録 or ログイン
  4. ログインページ

こういうページ遷移の時、ログインページでログインが完了したら、2番の商品詳細に戻したい場合のTipsです。

2の商品詳細は、1の親画面でタップされたデータを元に画面を初期化するので、予めroutes:に画面遷移を登録できないので、MaterialPageRouteを毎回呼び出して画面遷移をしています。

PopUntilで戻るためには、route.settingsに遷移履歴をセットしておかないと任意の画面まで戻れないので、以下のようなコードを書いて都度RouteSettingを指定します。これで戻れます。

遷移する時

Navigator.of(context).push(MaterialPageRoute(
    //これ
    settings: RouteSettings(name: ItemDetailPage.routeName),
    builder: (context) => ItemDetailPage(item: item),
));

戻る時

 Navigator.of(context).popUntil((route) => route.settings.name == ItemDetailPage.routeName);

良く出来てるよ、Flutterって。ネイティブアプリを作る面倒さを味わっているから、本当に楽しくネイティブアプリが書ける。設計思想も勉強になる。

大量のライブラリの面倒を見てくれるFlutterのありがたみ

Flutterを簡単に言えば、Dartでアプリを作るとiOS/Androidのネイティブアプリのプロジェクトに翻訳して、ネイティブアプリをワンソースで出来る仕組みです。

その中で、Flutterが提供してくれる多くのウィジェットや通信・DBのライブラリなどがあったりするんですが、それらはDartで書かれているわけではなく、ネイティブの言語で書かれたライブラリを使っています。OrganizerでアプリをAppStoreConnectに送るとよく分かる。Cocoapodsで外部ライブラリを色々Includeしてる。

f:id:gothedistance:20201215135327p:plain
Organizer

これらのネイティブで動作するライブラリをFlutterの公式がサポートしてくれているって、すごいことだよな。。。こんなにありがてぇことはないし、ちょっと怖くなるよ。。

Friendlyで「出来たらいいな」2つ

WPFアプリのE2Eテストを書く必要があり、Friendlyを触ってみた。

ishikawa-tatsuya.hatenablog.com

2つほど出来たらいいな、があった。

1. ViewModelのオブジェクトを取りたい

//WindowControl w 
var vm = w.AppVar.Dynamic().DataContext();
if(vm is HogeViewModel) {
   //falseになる
}

ってやると、dynamicで対象のビューのVMが取れるし、メソッドを叩くこともできる。ただ、VMの型にキャストすることが出来ない。VMの型にキャストできれば、Assertするのが楽になるので、やりたい。

2. HierarchicalTemplate

こちらに書いたが、メニューの子メニューをVisualTreeで辿れない。

qiita.com

とりあえず、書き散らかしただけですいません。。。

追記(2020.12.11)

vmへのキャストについてですが、別途[Serializable]を適用しないと、だめっぽい。

WPFGridのdataitemを取るのに、string code = ListGrid[1].itemcode とかやれば、stringで取れる。このstringで代入するときに、dynamicから型推論で型を解決するようなのだが、これがシリアライズによって値を持ってくるっぽいの。dynamicの機構のようだ。(怪しいけど、代入を実行することで、相手プロセスから自分のアプリのプロセスに展開されるっぽい)

HierarchicalTemplateのたどり方は、相変わらずうまくいかなかったが、ViewModelを叩くことが出来るので、メニューのUIを捕まえるのではなくViewModelのコマンドをそのまま実行して、ま、とりあえずなんだけど、テストケースを実行することは出来た。メニューや右クリックメニュー等は、Windowsのネイティブのオブジェクトらしくて、色々大変なようだ。

右クリックメニューを捕まえるの苦労したが、VisualTreeでWPFUserControlをセットして、x:Nameで辿ったら取れた。

PageObject.InputGrid.MouseDown(MouseButtonType.Left, 100, 50);
PageObject.InputGrid.MouseUp(MouseButtonType.Left, 100, 120);
PageObject.InputGrid.Click(MouseButtonType.Right, 100, 100);

var hoge = new WPFMenuItem(PageObject.ContentPage.Dynamic().StatusMenu); 
hoge.Click();

モーダルダイアログの操作もできた。モーダルの連鎖も、WaitForNextModalを連発することで、動いた。

 var async = new Async();
 button.EmulateClick(async);
 var dlg = w.WaitForNextModal();
 var dlg_po2 = new DialogPageObject(dlg);
 dlg_po2.OKButton.EmulateClick();
async.WaitForCompletion();

まず、PageObjectを作ろう

PageObjectは多分Seleniumの用語だと思うけど、テスト対象画面に存在するUIオブジェクトをVisualTreeから取得して変数に保存しておくこと。まず、これを当該画面を表示したら行う。PO初期化()っていう関数を作って、ClassInitilizeで呼び出すようにしてます。対象のオブジェクトがないと例外を出してくれるので(ナイス!)、そこで動かない場合はそこから。これができれば、7割ぐらい出来たようなもの。

DeployGateのコマンドラインツールのインストールでコケた

こんなログが出た

"xcrun clang -o conftest -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/universal-darwin19 -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/ruby/backward -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0 -I. -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE -D_DARWIN_UNLIMITED_SELECT -D_REENTRANT    -g -Os -pipe -DHAVE_GCC_ATOMIC_BUILTINS conftest.c  -L. -L/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/lib -L. -L/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.Internal.sdk/usr/local/lib   -arch x86_64   -lruby.2.6   "
In file included from conftest.c:1:
In file included from /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/ruby.h:33:
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/ruby/ruby.h:24:10: fatal error: 'ruby/config.h' file not found
#include "ruby/config.h"
         ^~~~~~~~~~~~~~~
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/usr/include/ruby-2.6.0/ruby/ruby.h:24:10: note: did not find header 'config.h' in framework 'ruby' (loaded from '/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks')
1 error generated.
checked program was:
/* begin */
1: #include "ruby.h"
2: 
3: int main(int argc, char **argv)
4: {
5:   return 0;
6: }
/* end */

Xcodeコマンドラインツールのパスが違ってたので直したらインストールできた

$ sudo xcode-select -switch /Library/Developer/CommandLineTools

これだけ。

あらざ〜す

MacのhomebrewでOpenSSLがビルドエラーになる場合の対処方法|TechRacho(テックラッチョ)〜エンジニアの「?」を「!」に〜|BPS株式会社