node-lambdaをes2017で書くまでの道のり

目的

aws lambdaのデプロイなどができるパッケージnode-lambdaでasync/await(es2017)を書けるようになる
プログラミングモデル (Node.js) - AWS Lambda

AWS Lambda は、現在以下の Node.js ランタイムをサポートしています。
* Node.js ランタイム v6.10 (ランタイム = nodejs6.10)

とあるように aws lambdaのNode.jsサポート環境は2018/02現在で6系までとなっており、async functionに対応していない

開発環境

$ node -v
v8.9.3
$ npm -v
5.6.0

テストフレームワークはmochaを使う

Babel7を使う

babelのGitHubリポジトリはベータ版である7系で説明されており、webページだと6系をつかって説明されている。ここの差異ですごくハマった。
「@babel/hogeのようにインストールするのがナウい」と聞いて躍起になって7系を使おうとしてしまったが、冷静に考えたら6系でよかった。
なぜベータ版に必死になってしまったのか。
ということでbabel7系を使う場合はGitHubリポジトリを読みながら進めるのがよい。公式のDocやググった情報を参考にするとハマる(ハマった)。

package.jsonに入れるもの

サポートされている環境に基づいて必要なプラグインとポリフィルを自動的に決定しますとのことなので、babel-preset-envを入れる。
(es2017対応環境で使うとそのままのコードが吐き出されて、トランスパイルされてないと思って焦った。es2015対応環境で使えばちゃんとそこで動くようにトランスパイルされる。)

npm-scriptsを書く

必要なパッケージをインストールしたらnpm-scriptsを書く

  "scripts": {
    "test": "mocha --recursive",
    "compile": "babel src --out-dir compiled",
    "package": "node-lambda package -H /compiled/index.handler",
    "deploy": "node-lambda deploy -H /compiled/index.handler"
  },

各コマンドを説明する

  • "test": "mocha --recursive"
    mocha.optsでbabel-registerを指定することでes2017で書いたテストを実行できる。 test/mocha.opts
--require @babel/register

mocha.optsを書かずに、"test": "mocha --recursive --require @babel/register"としても可。

  • "compile": "babel src --out-dir compiled"
    これでsrc/に入っているコードをトランスパイルしてcompiled/に出力する。

  • "deploy": "node-lambda deploy -H /compiled/index.handler"
    これでトランスパイルしたコードをlambdaのハンドラに指定してデプロイする。
    npm run packageも同様。 https://github.com/motdotla/node-lambda#deploy

    -H, --handler [index.handler] Lambda Handler {index.handler}

まとめ

これでlambdaでasync/awaitを使うことができるようになった。 ポイントだなと思ったことはトランスパイルしたコードをlambdaのハンドラに指定すること。
今まで見てきたlambdaのディレクトリ構成がlambda/index.js,lambda/lib/lib.js(index.jsでインポートされている)みたいな構成だったので、
webpackいるのかと思って悩んだけど、babelだけで書けた。

package.json

  {
  "scripts": {
    "test": "mocha --recursive",
    "compile": "babel src --out-dir compiled",
    "package": "node-lambda package -H /compiled/index.handler",
    "deploy": "node-lambda deploy -H /compiled/index.handler"
  },
  "devDependencies": {
    "@babel/cli": "^7.0.0-beta.39",
    "@babel/core": "^7.0.0-beta.39",
    "@babel/preset-env": "^7.0.0-beta.39",
    "@babel/register": "^7.0.0-beta.39",
    "chai": "^4.1.2",
    "mocha": "^5.0.0",
  }
}

create-react-appコマンドで作ったリポジトリをGitHubに上げる方法

ReactやVueなどのフレームワークcliから使う時、プロジェクトごと作ってくれる。

create-react-app my-app
create-react-native-app AwesomeProject
vue init webpack my-project

これをGitHub上にアップロードする方法をメモしておく。

  1. New repositoryでリポジトリを作る
  2. この時Initialize this repository with a READMEをチェックしない
  3. するとリポジトリを作った後に次に実行すべきコマンドが表示される
    …or push an existing repository from the command line にしたがいコマンドを実行する。 ローカルリポジトリを作ってリモートリポジトリを登録してからプッシュするという流れになる。

ローカルリポジトリで以下の手順を実行する。

createt-react-app my-app
cd my-app
git init
git remote add origin git@github.com:YOUR_USER_NAME/YOUR_REPOSITORY_NAME.git
git push -u origin master

コードを理解するときに個人的に気をつけているポイント

 

コードを読む時に気をつけているポイントの自分用メモ。(Githubを使っていることを想定)

 

ディレクトリ構成を理解する

ディレクトリ構成とファイル名を理解することでどんなプログラムか読み取ることができる。

フレームワークを使用している場合は、公式リファレンスにディレクトリの説明が書いてあるのでそれを読んで、ディレクトリの役割を理解する。

 

テストを読む

テストを読むことでインプットとアウトプットを理解する。

テストの文言からどのような事をおこなうプログラムなのか理解する。

 

コードを読む

ざっと関数だけを眺める。細かい実装は必要になったときに読む。

 

commitメッセージを読む

git blameを実行することで行毎のcommitメッセージを読むことができる。

git logを実行することでcommitログを新しい日付順に表示できる(デフォルトの場合)。

commitログからどのような変更か理解する。(Howの理解)

 

プルリクエストを読む

プルリクエストからどういった意図でプログラムが実装されているか理解する。(Whyの理解)

 

 

 

ログインをともなう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を使わないので事前に友人と練習した。

じつりき

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

以上です。