Anger Driven Development

備忘録でございます

ログインをともなうE2Eテストのコツ

ここ数週間ログインをともなうE2Eテストを実装していたので、 そこで得られた学びをロギングしておく。

環境

Node.js v6.11.0
Nightmare v2.10.0
Mocha v3.4.0

コツ

  1. テストはリトライさせる
    E2Eテストは、ネットワークやディスクIOなど外部要因が絡んでくる。 ユニットテストと比較し、実行時間もかかるし、外部要因によってテストが失敗したりと、テスト結果が安定しない。そのため、テストに失敗したらリトライするような仕組みを用意する。mochaの場合はretryオプションがあるのでそれを使った。E2Eのためのオプションらしい。
    RETRY TESTS

    This feature is designed to handle end-to-end tests (functional tests/Selenium…) where resources cannot be easily mocked/stubbed.

  2. beforEachはリトライ対象にならない
    上記リトライオプションだが、mochaのbeforEachには適用されない。 参考issue
    beforeEachなどはあくまでsetup/teardownに使ってくれ。フックで安定しないロジック使ってるなら、それを関数化してくれ。ってことらしい。
    今回、ログインをともなうE2Eだったので、beforEachでログイン処理をしていたが、関数化して、ログイン処理をリトライに対応させた。
    最初、beforEachもリトライ対象になると思い込んでいて時間のロスをしてしまった。

  3. テストシナリオの各工程にはフィードバックを
    たとえば、「ログアウトできること」というテストシナリオの場合、ログイン>ログアウトという工程を経ることになる。ログインにはフォーム入力、ボタンクリックなど、ログアウトにはログアウトのボタンクリックなど、各工程にも作業がある。つまり、一つのテストシナリオは複数の工程から構成される。今回の案件の場合は10~20程度の工程があった。その各工程で、フィードバックがないとテストでコケたときに、原因特定に時間がかかってしまう。
    実際にあったのは、「ログアウトできること」というテストシナリオで、アサーションが「ログイン時に付与されるクッキーが削除されていること」だったのだが、自分の書いたテストは、ログインできていないのに、次の工程に進んでしまっていた。当然ログインしていないので、ログイン時に付与されるクッキーは無い。そのため、アサーションも通ってしまっていた。
    結局、別のテストが落ちることで、ログインできていないことが判明したが、テストが落ちる原因特定にかなり時間をロスしてしまった。(そして、そもそもログインできていない原因は、上記にあげた外部要因によるものだった。)

まとめ

ブラウザオートメーションはphantom.jsとかでやったことがあったし、ユニットテストも書いたことがあったが、E2Eテストは初めてだった。
感想としては、自動化はできるが、ユニットテストと違って時間もかかるので、安定させるのが難しく、テクニックを要するなぁという感じ。
しかし、E2Eテストで事前に検知できたバグもあるので、書いたかいがあった。手動で検証するよりは、繰り返し実行できるし、時間もかからないので書く価値は有ると思う。ただ、安定稼働にコストも割くので、テストを書くのはコアな機能に限定するなど、見極めが必要だとも感じた。

forEachのネストが深くなって困った話

テストケースの組み合わせ

mochaで単体テストを書いていて、テストケースの組み合わせが増えて困った。
webシステムの単体テストだと、ドメインが異なるケース、クッキーの値が異なるケースなど、同じ機能を、異なるテストデータでテストすることがあると思う。
その時、テストケースを配列として扱って、forEachを用いてテストケース毎にテストを実行していた。 実際はループ毎にもう少し複雑な処理をしている。

describe('test', function () {
  Hoge.forEach((hoge) => {
    Fuga.forEach((fuga) => {
      Foo.forEach((ooo) => {
        it(`Hogeが${hoge},Fugaが${fuga},Fooが${foo}の時`, function () {

        });
      });
    });
  });
});

問題点

上のように書くと、ネストが深くなってコードが読みづらい。

解決策

そこで、 https://github.com/dankogai/js-combinatorics
を使う。 これを使って配列になっているテストデータの直積を取得すると、

const Combinatorics = require('js-combinatorics');
const cp = Combinatorics.cartesianProduct(Hoge, Fuga, Foo);
describe('test', function () {
  cp.toArray().forEach((element) => {
    it(`Hogeが${element[0]},Fugaが${element[1]},Fooが${element[2]}の時`, function () {

    });
  });
});

のように書ける。
テストデータの組み合わせでforEachのネストが深くなっている方は、使ってみてはどうだろうか。

転職して1ヶ月たった

1ヶ月で出てきたやつら

とくにJSとAWSの技術がうちのチームでは必要とされているみたい。支給PCはMacBookProの2016年モデル(メモリ16GB、HDD256GB)

変わったこと

  • 予定管理

Excelファイルをファイルサーバで共有 > グループウェア

「〇〇が編集中のため編集できません」ってよく表示されたのが懐かしい。

  • コミュニケーション

口頭、電話 > 口頭、Slack

客先からの電話にでることはなくなった。

上記Excelシートや、Redmineに入力するためのExcelシート > Googleスプレッドシート、Drow.ioを共有

  • 前職と比較してミーティングが多い(特にストレスはない)
  • Slackとの付き合い方を考えねば(仕事があるのに関係ないやりとりも見てしまう)
  • ヤクルトのお姉さんまたいた
  • ExcelやWordを記入することがほとんどなくなった

転職した人がするべきこと・考えるべきこと

  • 前職で感じたことは記録して残しておく

文化が違う場所に行くと、なれることに必死であっという間に前職のことを忘れてしまう。(自分もだいぶ忘れた。)自分が経験してきたこと・感じたことは、将来の意思決定でも非常に重要な要素の一つなので、同じ轍を踏まないように、記録しておくと良いと感じた。日常的、あるいは転職先の内定が出たときに、まとめて整理しておくのがベスト。

  • 転職活動を振り返る時間を確保する

転職先では「入ってみてどうか」という質問は何度か受けることになると思う。しかしそれはあくまで、現職側のことだけなので、自分の中で前職と比較してみて、自分はどういうことを仕事にしたいか、どういう環境で働くと楽しいか、ということを確認してみるのが良いと思う。

 

あとは生き残る。以上。

 

転職した

転職した

思ったことをつらつらとメモってゆく。

 転職活動における成功を定義する 

転職を成功させる秘訣は目的を明確に定義すること、これに尽きると思う。

仕事の軸は、

  • 内容
  • 給料
  • 人間関係

だと考えている。

これをどう変えるかを明確に定義する。

自分の場合は内容を第一に考え、目標通りの会社に転職できた。

ほかは、給料をX万まで上げる。〇〇さんや〇〇な人たちと一緒に働くなど。

転職の目的やゴールを決めず、なんとなく転職したり、嫌なことから逃げることだけにとらわれて転職しても、次の転職先でも結局同じことで失敗するだろう。

また、設定目標のすべてを満たすことは難しいという心構えを持つ。目的に優先順位をつけ、それを満たすような会社に転職する。

使った転職サイト

  • リクルートエージェント
  • レバテックキャリア
  • Green
  • FindJob
  • Paiza

エンジニア向けの転職サイト・エージェントがあるのでそれを使ったほうが良いと感じた。営業色が強く「とりあえず応募しましょう!」みたいなノリや、技術のことをあまり理解していない業者がいるのも事実。

あとは噂に聞いていた全く転職者のプロフィールを見ていない、一方通行のスカウトメールもあった。会社の評判を下げるだけなのでやめたほうが良いと思う。

市場価値

VisualBasic.NETの市場価値ェ。。。

転職エージェントからは「Web業界だとJavaPHPですよ!」(サーバサイド志望した時の反応)と散々言われた。
市場感(市場で求められるスキル、出ている求人数)に関してはエージェントが言うことは間違いないだろう。

ネット上で盛り上がっている、流行りの技術を使うのは一部ということであり、現実として2017年現在では上記に価値があるということか。

面談のむずかしさ

自分が思った以上に面接官に自分のことが伝わらない。

書類で説明したつもりでも、うまく伝わっていなかったり(忙しくて職務経歴書を読み込む暇もないのかも)、 自分がアピールしたい部分と違う部分を拾われたりして、なかなか言いたいことが伝えきれなかったように思う。

あとはSkype面談なるものを初めて経験した。面談前に音声やカメラのチェックは必須。自分の場合、普段Skypeを使わないので事前に友人と練習した。

じつりき

実力をつけるしかない。技術力、思考力、筋力を身つけてタフに生き残ってゆくしかない。やらなければやられる。これからもやっていく。

以上です。

 

TDDBC Toyama #1 2日目

いってきた

tddbc.connpass.com

メモ

リファクタリング

レガシーコードのジレンマ コードを変更するためには、テストを整備する必要がある。多くの場合、テストを整備するためには、コードを変更する必要がある。

レガシーコード改善ガイド読書メモ - Anger Driven Development

まさにこれが出た。

方法としては2つあって

  1. テストが無いままで安全に変更できる状況を考える
  2. 振る舞いを変えること無くテストできるか検討する
バグを直すということ

バグを直す===振る舞いが変わる
また自分がバグと思っているものは実は仕様かもしれない(途中からプロジェクトに参画して、ドキュメントなどが無い状況など)
動いているコードがすべての事実。コードの振る舞いを保つことを考える。変わりそうだったら上長に相談。

KPT

Keep

  • テストに保護されるの最高。これで変更のたびに「どうか壊れていませんように」と祈らなくてすむ。
  • リファクタリングの実践的な手法を学べた
     動いているコードをリファクタする時はまず振る舞いを保つように保護する。その時も、
    • 手で行わずIDEのリファクタ機能を使う
    • Lintをかける
    • 静的解析ツールを使って複雑度などを調べる
      などなるべく機械的に変更できる手段から実践していく。
  • ペアプロの楽しさを体験できた
    他人のコーディングを間近で見れる(IDEやエディタのホットキーなどのこだわりが垣間見れる、優秀なプログラマのコードの書き方、思考ステップなどの知見が得られる)

Problem

  • ペアプロの難しさを体験できた
    • 役割分担(ドライバー、ナビゲーターがなかなか交代できない)
    • 意識合わせ(今回だと、どういう思考でリファクタしていくかというのを逐一確認し、二人がその思考に合わせて行くのが難しかった。一人だと思考を柔軟に変更できる。)
  • コードを壊したくなる
    • 破壊衝動に駆られる。
      すぐにコードの振る舞いを変えるような変更を加えたくなったが、ナビゲータの方が制止してくれた。まだTDDが体に染み付いていない。
      対策:既存のコードの振る舞いを保証することを念頭に置く。焦らずTODOリストに落とし込む。現在動いているコードが絶対。2年前の先輩*1に敬意を表する。
  • TODOの粒度
    要件定義===テスト設計だと思うのだけど、その落とし込みが難しく感じた。どの程度の粒度で考えるか。粒度が細かすぎるとテストがやりづらいという話も上がった。

Try

  • 写経(TDDはセンスではなくスキルなので高めることができる)
  • TDDBCの途中になっている課題を終わらせる
  • テストファーストで作ってみる

雑感

1日目の深夜まで語り合ったり遊んでたりしたみたいで、早く寝ずに参加すればよかったと少し後悔。。。
朝1からクソコードレビューしたり、夜にこだわりのホットキーの話したり、エンジニアが集まってワイワイ話すのはほんとに楽しい。
是非また行きたいと感じた。

*1:動くクソコードを残して去った先輩のこと、合宿の会話中に何度も出てきた

TDDBC Toyama #1 1日目

いってきた

tddbc.connpass.com

メモ

「動作するきれいなコード」はあらゆる理由で価値がある

これに近づけるのが良いソフトウェア開発

「動作するコード」+「きれいなコード」に分解して考えると、

  • 「きれいなコード」

動かすまで問題がわからない。ソフトウェア開発は予測可能性が低い。

  • 「動作するコード」

これをきれいなコードに変更しようとすると

「動くから良いじゃないか」「変更して動かなくなったらどうする?」

という堕落、恐怖がある。

これに打ち勝つためにテストで保護する。

まずはテストで保護された動作するコードを書いて、それを編集してきれいなコードにしていく。
テストファースト:テストを書いてgreenになるように実装していく。)

TDDのサイクル

  1. 次の目標を考える (紙などに書き出すと良い)
  2. その目標を示すテストを書く
  3. そのテストを実行して失敗させる
  4. 目的のコードを書く
  5. 2で書いたテストを成功させる
  6. テストが通るまでリファクタリングを行う
  7. 1〜6を繰り返す

日々の作業に落とし込む

  • リファクタリングというタスクには呪いがある(終わらない)
  • リファクタリングは大事にしない,日々の作業に組み込む
  • よいソフトウェア開発はよいフィードバックサイクルから

テストとメンテナンス

  • テストのメンテナンスコスト
  • テストの理想はMESE
  • テストごとの重なり、漏れがあるとメンテナンスコスト高い
  • テストもメンテナンスしていく必要がある

実践

  • 仮実装(テストコードのテスト)
    例:真偽を返す関数でreturn trueなどして必ずテストを通るようにする。
    こうすることで、テストコードは間違っていないことを確認する。

  • TDDアンチパターンアサーションルーレット
    アサーションを羅列すると、どのテストで落ちたかわからなくなるので避ける。

  • 落ちるべきところで落ちることが理想(自分の予想と一致している)。

  • テストの対称性
    テスト件数にばらつきがあると、後任者から見ると段差がバラバラのはしごが残っているようにみえる。 テストを減らすことには判断コストがかかる。 テストケースを減らせるのはテスト担当者のみ。最後にテストの数を揃える。

TDDに必要なスキル

  • 問題を小さく分割する
  • 歩幅を調整する
  • テスト>仮実装>三角測量>実装
  • テスト>仮実装>実装
  • テスト>明白な実装
  • テストの構造化とリファクタリング

TDDはスキル

  • 一人から始められる
  • 才能ではなく習得可能
  • 量は質に添加する
  • 写経

「TDDの黄金の回転」はジョジョ7部ネタ。

レガシーコード改善ガイド読書メモ

電子書籍で買えばよかった・・・

1章 ソフトウェアの変更

ソフトウェア変更の4つの理由
  1. 要件追加
  2. バグ修正
  3. 設計の改善
  4. リソース利用の最適化(メモリ最適化)
4つの変更を加えると変化するもの
要件追加 バグ修正 リファクタリング 最適化
構造 変化 変化 変化 -
新機能 変化 - - -
機能 変化 変化 - -
リソース利用 - - - 変化
プログラムの振る舞いを保ったまま変更するときに考慮すべきこと
  1. どんな変更を行わなければならないか
  2. 変更が正しく行われたことをどうすれば確認できるか
  3. 何も壊していないことをどうすれば確認できるか

2章 フィードバックを得ながらの作業

システム変更の方法
  • 編集して祈る
  • 保護して変更する

後者がベストだがほとんど前者になっているのが現状

大規模テストによる問題点
  • エラー箇所の特定が困難
  • 実行時間が長くなる
  • カバレッジの把握が困難、新規コード追加時に高いレベルのテストを書く必要があるので作業量が増える
優れた単体テストの条件
  • 実行が速い
  • 問題箇所の特定がし易い

実行に0.1秒もかかる単体テストは、遅い単体テストである。

  • 10テスト/クラス×0.1秒×3000クラス=3000秒=50分
  • 10テスト/クラス×0.01秒×3000クラス=300秒=5分

2~3時間ごとにすべてのテストを動かす場合を想定すると0.1秒だときつい。

単体テストと分けて考えるべきもの
  1. データベースとやり取りする
  2. ネットワークを介した通信をする
  3. ファイルシステムにアクセスする
  4. 実行するために特別な環境設定を必要とする(環境設定ファイルの編集など)

上記テストは価値があるが、単体テストとは分けて考える

レガシーコードのジレンマ

コードを変更するためには、テストを整備する必要がある。多くの場合、テストを整備するためには、コードを変更する必要がある。

レガシーコードの変更手順
  1. 変更点を洗い出す
  2. テストを書く場所を見つける
  3. 依存関係を排除する
  4. テストを書く
  5. 変更とリファクタリングをおこなう

レガシーコード改善ガイド (Object Oriented SELECTION)

レガシーコード改善ガイド (Object Oriented SELECTION)