Cypress with TypeScriptにカスタムコマンドを追加する

Cypressには処理を共通化したりするために、独自のCommandを追加できる機能がある。

docs.cypress.io

例えば、ログインの機能をカスタムコマンドとして定義することで、ログインが必要なテストケースの際に毎回ログイン処理を書く必要がなくなる。

このカスタムコマンドを追加する際、Typescriptの場合は型定義ファイルを追加してあげる必要がある。

型定義ファイルの追加

カスタムコマンドは、下記のように追加することで、テストケース側で呼び出すことができるようになる。

// cypress/supports/commands.ts
Cypress.Commands.add("login", (email, password) => { ... });
// cypress/e2e/example.cy.ts
describe('example', () => {
  it("test case", () => {
    cy.login('xxx', 'xxx');
  });
});

型定義ファイルがないと、cy.loginなんてメソッドはないぞと怒られるし、そもそもCypress.Commands.addの第一引数のloginという文字列も、そんな文字列受け取れませんぞと怒られる。

これは、Cypress.Commands.addが下記のように、事前に定義されている値しか受け取らないようになっているからである。

add<T extends keyof Chainable>(name: T, fn: CommandFn<T>): void

なので、型定義ファイルを追加して、loginを含めたInterfaceを定義する必要がある。

// cypress/types/index.d.ts
declare namespace Cypress {
  interface Chainable {
    login(email: string, password: string): Chainable<void>;
  }
}

これで問題なくカスタムコマンドを追加できる。

新しく入社した方とはとりあえず1on1しとこう

以前、入社してきたばかりの方が古株の自分に対して「ちょっとお話しするお時間いただけませんか?」と声をかけてくださり、30分程度雑談をしたことがある。

歓迎会としてのランチとか飲み会とかではなく、入社後の第一手として、業務の時間を使って一対一でちゃんと話す、ということが、その後一緒に働いていく上ではとても大きなことだなと思った。

この体験が自分にとってすごく良かった記憶として残っており、新しく入社してくださった方とは、どんな業種であれ、最初にちゃんと話す、ということを積極的にしたいなと思うようになった。

なので、今日新しく入社した方と初めて会うタイミングがあったので頑張って声をかけた。ちょっと緊張したけど。

まあ正直方法はなんでもいいんだけど、相手を知る努力も、自分を知ってもらう努力も、どちらも怠ってはいけないなと。

初手1on1。いい手だと思う。

SCRUM BOOT CAMPを読んだら、SCRUMはチームとして成長するためのフレームワークであるということを学べた

SCRUMはただの柔軟なソフトウェア開発の手法だと思っていた

特にSCRUMの開発経験はないので、SCRUMという名前は知っていてもそこまで具体的には知らなかった。
だから、ソフトウェア開発をアジャイルに進めていくための手法、という程度の認識しかしていなかったのだけど、SCRUMの本を読んでみると、もちろんそうなんだけど、それだけではない、というふうに自分は感じた。

むしろ、アジャイル開発の中で、チームというものをどう成長させていくのか、どうやってチームが強くなっていくのかという点に重きをおいているように思えた。

SCRUMの基本をまとめてみる

一旦自分の整理のためにSCRUMの基本をまとめてみる。

3つのロール

SCRUMには3つのロールが存在する。

  1. プロダクトオーナー
  2. 開発チーム
  3. スクラムマスター

そして、この3つのロールがプロダクトバックログという一つの柱を軸に、それぞれが役割を発揮していく。

プロダクトバックログとはプロダクトの実現したいことを、実現したい順に並べたもののこと。

プロダクトオーナーは、プロダクトバックログの順番に責任を持つ。
プロダクトバックログの順番を並べ替えられる権限を持つのは、プロダクトオーナーだけである。

開発チームは、プロダクトバックログの項目をどうやって実現するのかということに責任を持つ。

スクラムマスターは、プロダクトオーナーと開発チームがうまくSCRUMのフレームワークにのれるようにサポートすることに責任を持つ。

開発の流れ

実際の開発は、スプリントと呼ばれる期間で区切った単位で進めていく。

  1. スプリントが始まる際に、プロダクトバックログから今回のスプリントで開発を進める項目を決めて、タスクに落とし込んでいく。これを、スプリントプランニングと呼ぶ。
  2. スプリント中は毎日、今やっていること、昨日やったこと、困っていることを共有する。これを、デイリースクラムと呼ぶ。
  3. 開発チームは過去のスプリントと、現在のスプリントで開発したものを合わせて、動作して検査可能なものを作成する。この成果物を、インクリメントと呼ぶ。
  4. スプリントの終盤でインクリメントのデモを行い、フィードバックを得る。これを、スプリントレビューと呼ぶ。
  5. 最後に、次のスプリントに向けてもっと改善したほうが良いことなどを話し合う。これを、スプリントレトロスペクティブと呼ぶ。

この一連の流れを1スプリントごとにまわしていき、開発を進めていく。

見通しを立てるために

1スプリントを終えたとき、開発チームが1スプリントでどれだけのことができるのかということが見えてくる。

この1スプリント単位の作業量のことをベロシティと呼ぶ。

そして、同じようにプロダクトバックログの各項目にも、どれだけの作業量が必要そうかを、開発チームは見積もる。これを、ストーリーポイントと呼ぶ。

プロダクトバックログの各項目のストーリーポイントがわかり、開発チームのベロシティがわかると、バックログのどこまでが何スプリントで終わりそうなのか、という見通しが立てやすくなる。

プロダクトバックログの見直し

スプリントをまわしていくためには、プロダクトバックログから項目を選択し、タスクに落とし込んでいく必要がある。

プロダクトバックログの項目をタスクに落とし込むためには、バックログの項目をある程度詳細に詰めておく必要がある。

また、項目は本当にこれでいいのか、並び順は本当にこれでいいのか、といったような見直しも常にする必要がある。

このプロダクトバックログの見直しのことをプロダクトバックログリファインメントと呼ぶ。

プロダクトバックログリファインメントは特にこのタイミングで必ずやらないといけない、というようなルールは定められていない。ただ、プロダクトバックログの更新が止まってしまうのは絶対によくない。


と、ものすごくざっくりまとめてみると、こんな感じだと思う。
この理解をもとに、自分の感想をまとめてみる。

ロールの明確さ

まず、3つのロールについて。
ロールが少ない。そして、それぞれの責任範囲が明確になっていることが、素晴らしい。

特に、プロダクトオーナーだけがバックログの順番を並び替える権限を持つ、という点。
よくある「で、誰がこれを決めるんでしょうか?」ということを聞かなくてもよくなりそう。それだけでだいぶ嬉しい。

徹底したタイムボックス

SCRUMでは、各イベントに対してタイムボックスが設けられている。

スプリントは、1週間から1ヶ月の間で定める。
そして、スプリントの長さに応じて、スプリントプランニングにかけられる時間などが定められている。(1ヶ月スプリントであれば8時間など)
デイリースクラムは必ず15分以内に終わらせる。

時間をフレームワークによって定められることで、「この時間ってどれぐらい使っていいんだっけ?」という無駄な脳のリソースを使わなくてもすむ。これもでかい。

名前がつけられることのやりやすさ

SCRUMというフレームワークにのっからずとも、SCRUMの中のイベントのようなことは、よくやっているはずである。

次になにするかを話し合ったり、タスクばらしをしたりなど。

だけど、それに対して「プロダクトバックログリファインメント」だったり「スプリントプランニング」という名前がはっきりとつけられることで、途端にやりやすくなる。
目的がはっきりするし、今何をやっているか迷わなくなるからだと思う。

自分たちの力を知り、自分たちで解決し、自分たちで強くなっていく

ここまでの感想は、SCRUMだからというよりも、フレームワークに乗っかるってそういうメリットがあるよね、という部分が強い気がする。

でも、タイトルに書いたように、SCRUMはチームとして成長するためのフレームワークなんだなと感じた。

ベロシティというのは、まさに、今の開発チームの開発力の見える化であって、スプリントを繰り返していくときっと上がっていく値なんだと思う。

それはスプリントというものが、前回のスプリントよりも今回のスプリントを少しでも良いスプリントにするという信念のもとに組まれているように見えたからである。

そして、この信念を実行するのは、開発チームそのものなのだ。

そのためのスプリントプランニングであり、そのためのデイリースクラムであり、そのためのスプリントレトロスペクティブなのだ。

本の中で、デイリースクラムは進捗報告の場ではなく、自分たちで問題を見つけるための場であると説明されていた。

毎スプリント、自分たちで計画を立て、自分たちで問題を見つけ解決し、自分たちで振り返って次に活かしていく。

SCRUMはそんなフレームワークだからこそ、うまく使えれば強いチームに成長していけるのかなと思った。

読んでみて

とてもよかった。今の自分に必要だと思える内容だった。

Flutter触り始め

この度、新しい仕事で必要になりそうになったので、Flutterを触り始めた。

iOSAndroidの2つのアプリを1つのソースで開発できるというのは、ReactNativeやらUnityやらいろいろな方法はあるけれど、あんまりいい印象は無い。
いい印象は無いと言うけれど、そもそもそんなに知らない。印象もなにもない。
でも結局はどこかで無理が生じて、ネイティブが一番だねってなる印象だ。あれ、印象があったぞ。

ここでは、自分がFlutterのドキュメントを読み進めていく中で理解したことを、自分なりの言葉で書き記せば、その分頭に入ってくるのではないかなと思ったので、雑に書き記していこうと思う。つまり中身はたいしてない。この記事を読んでいる人がもしいるとしたら、ここで止めておいてFlutterの公式ドキュメントを読みに行くことを強くおすすめする。

なぜなら、Flutterのドキュメント、めちゃくちゃちゃんとしているからである。

flutter.dev

チュートリアルが手厚い。その手の本を読んでいるように、手を動かしながら学べる。
そして、ちゃんと動くものを作ることができる。楽しい。楽しいは強い。

あと第一印象として、とてもとっつきやすい気がする。
これはでかい。昨今のframeworkの第一歩目の険しさたるやいなや。Hello Worldが全然Helloしてこない。
しかし、Flutterはいとも簡単にWorldがHelloしてきた。emulatorで動いたのだ。あれ、これアプリ作れるじゃんってひとまず錯覚したぞ。すごいぞGoogle。ありがとうGoogle

もちろんこれはまだ序盤も序盤で、山を登るにつれてどんどん難しくなっていくのかもしれない。
富士山をちょろっと登っただけで、「あれ?富士山てこんなものなの?登頂なんて余裕じゃん。」って調子こいてたら、ベテラン登山家にボコボコにされるだろう。
でも入山は本当に簡単だった。入山さえしてしまえば、とりあえず登ることはできる。だからとりあえず登ってみよう。途中で崖に落ちるかもしれないし、降りたくなるかもしれないけど。

そう、かの有名な登山家は言った。

「なぜ、山に登るんですか?」
「そこに、山があるからです。」

それと一緒です。

「なぜ、Flutterを触り始めたんですか?」
「仕事があるからです。」

最初に書きましたね。仕事で必要になったので触り始めたのです。
さぁ、登るぞ〜

iOSアプリ開発の証明書周りがよくわかっていないので自分なりに整理してみた

iOSアプリの証明書周り、非常にわかりにくい。

開発時に毎回どうだったっけ?って忘れてしまうので、自分なりに整理してみました。

証明書周りの構成

証明書周りの構成は4つに分類されています。

  1. App ID
  2. 証明書
  3. 端末
  4. プロビジョニングファイル

developmentとdistribution

証明書やプロビジョニングファイルには、開発時に使用するdevelopmentと、リリース時に使用するdistributionの2つの種類があります。

  • development
    • 自分のxcodeで開発をして、実機にインストールする時などはdevelopment
  • distribution
    • アプリをarchiveして配信する場合はdistribution

App ID

App IDはアプリごとに発行するIDです。

xcodeのプロジェクト内のBundle IDと、このApp IDを合わせる必要があります。

このIDに対して、そのアプリ上で使用するサービスを選択します。(プッシュ通知や、Apple Payなど)

証明書

開発時にはdevelopment用の証明書を使用して、実機にインストールしたりします。

配信用にipaファイルを作成する場合は、production用の証明書が必要です。

この証明書を作る時に、キーチェーンを使って秘密鍵やらなにやらを用意する必要があります。

証明書作成後、Appleの管理画面からは証明書自体はDownloadできますが、秘密鍵はDownloadできませんので、必ず無くさないようにする必要があります。

※アプリ配信用の証明書の他にも、Push通知用の証明書など複数種類があります。

端末

Ad Hoc版の際にインストール可能な端末を指定するために、端末を登録する必要があります。

端末の登録には端末ごとに発行されているUDIDが必要です。

端末は最大で100件まで登録が可能。

端末を指定してDisableにすることは可能だが、削除することは基本的にできない。(100件登録してしまうと、新しい端末を登録できなくなってしまう。)

1年に1度更新のタイミングで、端末を整理することができ、そのタイミングのみ端末の削除が可能になる。

プロビジョニングファイル

プロビジョニングファイルは、下記のデータにひも付き、アプリごとに発行します。

  • App ID
  • 証明書
  • 端末

プロビジョニングファイルがいろいろな情報を持っており、アプリをビルドする時にそのプロビジョニングファイルを指定してビルドさせます。

ただ、プロビジョニングファイルだけでビルドできてしまうと、セキュリティ的によろしくないため、プロビジョニングファイルに紐付いている証明書を持っていなければビルドができない、という仕様です。

また、プロビジョニングファイルが持っているApp IDと、ProjectのBundle IDが一致しないとビルドができません。

持っている情報が変わればプロビジョニングファイルも変わる

検証する端末が増えたりした時など、管理画面からプロビジョニングファイルの編集を行います。

この結果、新しいプロビジョニングファイルには新しいデータが書き込まれているため、古いプロビジョニングファイルと交換する必要があります。

よく、管理画面で端末を追加したのにインストールできない、というケースがありますが、これはビルド時のプロビジョニングファイルが変わっていない可能性が高いです。

Ad Hoc

本番の証明書を使用して、限られた端末にのみインストールできるようにするものを、Ad Hoc版といいます。

Test flightやCrashlyticsで検証用のアプリを配信する際には、Ad Hoc版のプロビジョニングファイルを作成して指定する必要があります。

ものすごいざっくりですが、概念をまとめてみました。

【vim】正規表現のマッチする範囲を指定して文字列置換をもっと簡単にする

\zs\zeを使えば、正規表現のマッチする範囲を指定できて、置換がとても楽になる。

例えば、

<div>ABCDEF</div>
<p>123456</p>
<div class="sample">abcdef</div>

という文字列があって、divタグの中身だけ変えたい時。

普通にやると、ちょっとめんどくさい正規表現を書かないと置換できません。

%s/\(<div.*>\).\+\(<\/div>\)/\1REPLACE\2/

だいぶごちゃごちゃしてます。

でもこれを、\zs\zeを使えば、こうなります。

%s/<div.*>\zs.\+\ze<\/div>/REPLACE/

マッチする部分が、\zsから\zeで囲んだ部分だけになるので、()でグループ化して後方参照させる必要がなくなります。

便利すぎる。

参考記事

僕が一番 Vim の正規表現をうまく使えるんだ(\zs \ze 編) - TIM Labs

Vimの正規表現 “\v”, “\zs”, “\ze” | blog.remora.cx

【swift】Viewの一部を角丸にする

swiftで角丸のViewを作るには、layerのcornerRadiusを設定すれば簡単にできますが、これだと4隅すべてが角丸になってしまいます。

上だけを角丸にしたい時などには、一部だけ角丸のパスをUIBezierPathで作成し、CAShapeLayerに設定します。

developer.apple.com

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .grayColor()

        // 上だけ角丸にするViewを用意 
        let view1 = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
        view1.backgroundColor = .greenColor()

        view.addSubview(view1)
        view1.center = view.center

        // TopLeft, TopRightを5px丸めたパスを生成
        let maskPath = UIBezierPath(roundedRect: view1.frame, byRoundingCorners: [.TopLeft, .TopRight], cornerRadii: CGSize(width: 5, height: 5))
        
        // CAShapeLayerを生成し、先ほど生成したパスをセットする
        let maskLayer = CAShapeLayer()
        maskLayer.path = maskPath.CGPath

        // 対象のViewのlayer.maskにCAShapeLayerをセットする
        view1.layer.mask = maskLayer
    }

}

f:id:w6500:20160527002452p:plain:w300