120
第一回 目黒スタートアップ勉強会 Takuma Morikawa / eureka, inc. Copyright © 2009-2015 eureka, inc. All rights reserved.

pairsでのAngularJS x TypeScript x e2e @めぐすた#1

Embed Size (px)

Citation preview

第一回 目黒スタートアップ勉強会

Takuma Morikawa / eureka, inc.

Copyright © 2009-2015 eureka, inc. All rights reserved.

じこしょうかい

About me

Copyright © 2009-2015 eureka, inc. All rights reserved.

なまえ Takuma Morikawa しょぞく たんとう (Go, JS, たまにインフラ)

じこしょうかい

エウレカについて

About eureka

株式会社エウレカ

専門分野以外の領域 でも活躍できる

エンジニア!  50人

非エンジニア!  25人

事業内容 自社サービスの企画・開発・運営 ・ オンライン・デーティング・サービス「pairs」 ・ カップル専用アプリ「Couples」

海外拠点 ・ 唯麗家股份有限公司(台湾) ・ EUREKA SG Pte. Ltd.(シンガポール)

社員数: 75名 インターン・業務委託含む

オンライン・デーティング・サービス「pa i rs」

12年9月 13年3月 13年9月 14年3月 14年9月 15年3月

225万

。゚+\\200万会員突破//+.゚

リリース 2012年 10月

会員数 210万人

プラットフォーム PC/SP/iOS/Android

135万

45万

90万

190万 2年で 200万人

カップル専用アプリ「Coup les」

。゚+\\200万DL突破//+.゚

リリース 2014年 5月

DL数 210万DL

プラットフォーム iOS/Android

14年5月 14年10月 15年3月

215万

170万

130万

85万

45万

国内 No.1

Today's Talk

pairs golang development from scratch

Go言語でいかにして新しいpairsを作っているのか

pairs golang development from scratch

Go言語でいかにして新しいpairsを作っているのか

AngularJS x TypeScript

x

AngularJS x TypeScript

x

Why Angu la rJS?

・おそらく採用の第一候補に上がるJS FW

・元々pairsでも使っていた  - メンバーはみんな書ける(はず)

・状態遷移が非常に楽 - pairsと相性が良い

・SPAにしたい

 - アクション増加 => マッチング数増加

Why Angu la rJS?

AngularJS x TypeScript

x

Why TypeScr ip t?

・話せば長くなります。

Why TypeScr ip t?

あるよく晴れた日の午後…

one day, very sunny day afternoon…

ʕº ºʔ<「うーん今日もいい天気」

Why TypeScr ip t?

ʕº ºʔ<「うーん今日もいい天気」

ʕº ºʔ<「こんな日はJavaScriptに限る」

Why TypeScr ip t?

ʕº ºʔ<「うーん今日もいい天気」

ʕº ºʔ<「こんな日はJavaScriptに限る」

ʕº ºʔ<「ん…なんだこれは…」

Why TypeScr ip t?

Why TypeScr ip t?

ʕº ºʔ<「shit…!」

Why TypeScr ip t?

ʕº ºʔ<「shit…!」

ʕº ºʔ<「どうしたもんか…」

Why TypeScr ip t?

ʕº ºʔ<「shit…!」

ʕº ºʔ<「どうしたもんか…」

ʕº ºʔ<「 」

Why TypeScr ip t?

ʕº ºʔ<「shit…!」

ʕº ºʔ<「どうしたもんか…」

ʕº ºʔ<「 コミット時に強制lintだ!」

Why TypeScr ip t?

$ jshint

Why TypeScr ip t?

$ jshint hoge.js: line 17, col 187, Line is too long. hoge.js: line 25, col 174, Line is too long. hoge.js: line 27, col 126, Line is too long. hoge.js: line 31, col 126, Line is too long. hoge.js: line 145, col 124, Line is too long. hoge.js: line 22, col 42, Expected '===' and instead saw '=='. hoge.js: line 24, col 44, Expected '===' and instead saw '=='. hoge.js: line 306, col 11, Missing semicolon. …

Why TypeScr ip t?

$ jshint | wc -l

Why TypeScr ip t?

ʕº ºʔoo0(…)

Why TypeScr ip t?

ʕº ºʔoo0(…)

ʕº ºʔoo0(さて、日報書いて帰りますか…)

Why TypeScr ip t?

そして月日は流れ…

someday…

2014年 10月

Oct, 2014

ʕº ºʔ(私=森川)      (ボス)ʕʘʘʔ

Why TypeScr ip t?

「森川さん」>ʕʘʘʔ

Why TypeScr ip t?

「森川さん」>ʕʘʘʔ

「pairsっていうサービスがあるので」>ʕʘʘʔ

Why TypeScr ip t?

「森川さん」>ʕʘʘʔ

「pairsっていうサービスがあるので」>ʕʘʘʔ

「Goで書き直して下さい」>ʕʘʘʔ

Why TypeScr ip t?

ʕº ºʔoo0(shit…)

Why TypeScr ip t?

ʕº ºʔoo0(shit…)

ʕº ºʔ<「は、はい!分かりました!」

Why TypeScr ip t?

「DBスキーマはこんな感じで。」>ʕʘʘʔ

Why TypeScr ip t?

「DBスキーマはこんな感じで。」>ʕʘʘʔ

「APIの定義はJSON Schema見てください」>ʕʘʘʔ

Why TypeScr ip t?

「DBスキーマはこんな感じで。」>ʕʘʘʔ

「APIの定義はJSON Schema見てください」>ʕʘʘʔ

「リリースはX月Y日です」>ʕʘʘʔ

Why TypeScr ip t?

「リリース日は何としても死守して下さい」>ʕʘʘʔ

Why TypeScr ip t?

「リリース日は何としても死守して下さい」>ʕʘʘʔ

「さもなくばサービスが死にます」>ʕʘʘʔ

Why TypeScr ip t?

「リリース日は何としても死守して下さい」>ʕʘʘʔ

「さもなくばサービスが死にます」>ʕʘʘʔ

(私の首も飛びます)0ooʕʘʘʔ

Why TypeScr ip t?

「リリース日は何としても死守して下さい」>ʕʘʘʔ

「さもなくばサービスが死にます」>ʕʘʘʔ

(私の首も飛びます)0ooʕʘʘʔ

「それとGWに合宿入れておきましたw」>ʕʘʘʔ

Why TypeScr ip t?

ʕº ºʔoo0(目が笑ってない…)

Why TypeScr ip t?

ʕº ºʔoo0(目が笑ってない…)

ʕº ºʔoo0(うう…デスマは嫌だ…)

Why TypeScr ip t?

ʕº ºʔoo0(目が笑ってない…)

ʕº ºʔoo0(うう…デスマは嫌だ…)

ʕº ºʔ<「 」

Why TypeScr ip t?

ʕº ºʔ<「そうだ!」

Why TypeScr ip t?

ʕº ºʔ<「そうだ!」

ʕº ºʔ<「iOS/Androidと同じAPI使って」

Why TypeScr ip t?

ʕº ºʔ<「そうだ!」

ʕº ºʔ<「iOS/Androidと同じAPI使って」

ʕº ºʔ<「PCとスマホで同じJSコード使えば」

Why TypeScr ip t?

ʕº ºʔ<「そうだ!」

ʕº ºʔ<「iOS/Androidと同じAPI使って」

ʕº ºʔ<「PCとスマホで同じJSコード使えば」

ʕº ºʔ<「工数を削減できるぞ!」

Why TypeScr ip t?

∴ SPA

therefore SPA

ここまで一切TypeScriptの話 無し

TypeScript

ʕº ºʔ<「型が欲しい。」

ʕº ºʔ<「コンパイルしたい。」

ʕº ºʔ<「無駄な学習コスト払いたくない。」

ʕº ºʔ<「でもいざとなったらJSに戻すかも」

Why TypeScr ip t?

「でも、お高いんでしょ?」

「でも、お高いんでしょ?」 「お任せ下さい!」

ʕº ºʔ<「型が欲しい。」

=> いざとなったらanyも使えて良い。

Why TypeScr ip t?

ʕº ºʔ<「コンパイルしたい。」

=> コンパイルの安心感。

Why TypeScr ip t?

ʕº ºʔ<「無駄な学習コスト払いたくない。」

=> ES6のシンタックス。

Why TypeScr ip t?

ʕº ºʔ<「でもいざとなったらJSに戻すかも」

=> おまかせくだ(ry

Why TypeScr ip t?

AngularJS x TypeScript

x

・ディレクトリ構成

↓を参考にしています Best Practice Recommendations for Angular App Structure

Angu la rJS

app/ - 機能群/ - ログイン/ - 検索/ - controller/ - service/ - directive/ - routing.ts - 共通ライブラリ/ - e2eテスト/

Angu la rJS d i rec to ry s t ruc tu re

Angu la rJS rou te

module pairs.search {!!angular.module('pairs.search', [!! !'ui.router',!! !'ipCookie',!!]).config([!! !'$stateProvider', !! !'$urlRouterProvider', !! !'$locationProvider', !! !states!!]).service('SearchService', [!! !'HttpService', !! !'$q', !! !'ipCookie', !! !SearchService!!]).controller(controller)!!.directive(directive);!

Angu la rJS rou te

!function states(!! !$stateProvider: ng.ui.IStateProvider,!! !$urlRouterProvider: ng.ui.IUrlRouterProvider,!! !$locationProvider: ng.ILocationProvider!!) {!! !$stateProvider!! ! !.state('search', {!! ! ! !url: '^/search/:type/{page:int}',!! ! ! !controllerAs: 'ctrl',!! ! ! !controller: controller.SearchController,!! ! ! !templateUrl: '/search/hoge'!! ! !});!!}

・ロジックは、ほぼ書かない前提

・ディレクティブ用のshared controllerな感じ

=> RIP controller in Angular2

Angu la rJS con t ro l le r

・可搬性の高いテンプレートにしたい

・インタラクティブなイベント・発火処理はこちら

・必要なデータだけコントローラーからもらう

Angu la rJS d i rec t i ve

・色んなロジックを書く

・様々なdirective間で使い回しできるようにする

Angu la rJS serv ice

・色んなロジックを書く

・様々なdirective間で使い回しできるようにする

=> テストがしやすくなる

Angu la rJS serv ice

・ベーシックにKarma & Jasmine

Angu la rJS un i t tes t ing

・ベーシックにKarma & Jasmine(の予定)

Angu la rJS un i t tes t ing

・ベーシックにKarma & Jasmine(の予定) => 実はまだ整っていない…

Angu la rJS un i t tes t ing

・ベーシックにKarma & Jasmine(の予定) => 実はまだ整っていない…

・TSコンパイルは通るけど、karma実行時にTSエラーになる

・駄目だったらJSで書く予定

Angu la rJS un i t tes t ing

・ベーシックにKarma & Jasmine(の予定) => 実はまだ整っていない…

・TSコンパイルは通るけど、karma実行時にTSエラーになる

・駄目だったらJSで書く予定(来週のタスク)

Angu la rJS un i t tes t ing

・Protractor

Angu la rJS e2e tes t ing

・Protractor

Angu la rJS e2e tes t ing

・Protractor

- AngularJS用のe2eテストFW

- 非同期処理をいい感じで待ってくれる

Angu la rJS e2e tes t ing

【ディレクトリ構成】

- 機能テスト/ // 単体テスト

- シナリオテスト/ // 結合テスト

- ページオブジェクト/

- ログインページ/

- 検索ページ/

- helper.js

- config.js

Angu la rJS p ro t rac to r d i rec to ry

【ディレクトリ構成】

- 機能テスト/ // 単体テスト

- シナリオテスト/ // 結合テスト

- ページオブジェクト/ - ログインページ/

- 検索ページ/

- helper.js

- config.js

Angu la rJS p ro t rac to r d i rec to ry

・特定のHTMLの塊の機能に関する処理を書く

・テストコードから直接HTML elementは操作しない

・HTMLに関する処理はページオブジェクトに書く

・1ページ1オブジェクトじゃなくてもOK

Angu la rJS page ob jec t

Angu la rJS page ob jec t var LoginPage = function() {!

!// ログインボタンのエレメント! this.loginButton = element(by.id('login'))!!

!// ログインページを開く! this.open = function(){! browser.get('/#/login');! }!!

!// ログインボタンを押下する! this.clickLoginButton = function(){! var loginButton = new LoginPage().loginButton;! return browser.wait(function(){! return loginButton.isPresent();! }).then(function(){! return loginButton.click();! });! };!};!module.exports = new LoginPage();

【ディレクトリ構成】

- 機能テスト/ // 単体テスト

- シナリオテスト/ // 結合テスト

- ページオブジェクト/

- ログインページ/

- 検索ページ/

- helper.js

- config.js

Angu la rJS p ro t rac to r d i rec to ry

Angu la rJS he lper

// time_helper.js!!var TimeHelper = function(){!

!// 現在のUNIXTIMEを取得する!!this.getCurrentUnixtime = function(){!! !return Math.floor(new Date/1000);!!};!!!

};!!module.exports = TimeHelper;

Angu la rJS he lper

// helper.js!!var Helper = function(){};!!// 外部で定義したヘルパー関数をHelperへ詰め込む(mixin的な?)!require('./time_helper').call(Helper.prototype);!require('./selectbox_helper').call(Helper.prototype);!require('./hoge_helper').call(Helper.prototype);!!module.exports = new Helper();

Angu la rJS page ob jec t w / he lper

// login.po.js!!var LoginPage = function() {! // load helper!

!var helper = require('../helper');!!// 都道府県のセレクトボックス !

this.stateSelect = element(by.model('vm.state_id'));!!

!// セレクトボックスから指定した都道府県を選択する! this.selectStateByName = function(name){! var el = new LoginPage().stateSelect;! helper.selectDropdownByText(el, name);! }!};!!module.exports = new LoginPage();

・TSじゃなくてJS

- jQueryの型定義ェ…

・CircleCIでも回す

- ローカルだとこけないのに、CircleCIだと失敗

Angu la rJS e2e tes t ing

・CircleCIの失敗

Angu la rJS e2e w/ C I

・CircleCIの失敗 - Slackに通知行く - エラーに慣れる - 非常に困る

Angu la rJS e2e w/ C I

・CircleCIの失敗 - Slackに通知行く - エラーに慣れる - 非常に困る

・なんで失敗するのか

Angu la rJS e2e w/ C I

・CircleCIの失敗 - Slackに通知行く - エラーに慣れる - 非常に困る

・なんで失敗するのか

- e2eがタイムアウトしてる - Angular以外のページ

Angu la rJS e2e w/ C I

・CircleCIの失敗 - Slackに通知行く - エラーに慣れる - 非常に困る

・なんで失敗するのか

- e2eがタイムアウトしてる - Angular以外のページ => Facebookのログインポップアップ

Angu la rJS e2e w/ C I

ʕº ºʔ<「Facebookのポップアップだって?!」

Angu la rJS e2e w/ C I

ʕº ºʔ<「Facebookのポップアップだって?!」

ʕº ºʔ<「よーし、おじさん頑張っちゃうぞ」

Angu la rJS e2e w/ C I

5時間後…

5 hours later…

ʕº ºʔ<「無理」

Angu la rJS e2e w/ C I

ʕº ºʔ<「無理」

ʕº ºʔ<「ログアウト機能追加しても無理」

Angu la rJS e2e w/ C I

ʕº ºʔ<「無理」

ʕº ºʔ<「ログアウト機能追加しても無理」

ʕº ºʔ<「クッキー全削除でも無理」

Angu la rJS e2e w/ C I

ʕº ºʔ<「無理」

ʕº ºʔ<「ログアウト機能追加しても無理」

ʕº ºʔ<「クッキー全削除でも無理」

ʕº ºʔ<「シークレットモード(Chrome)でも無理」

Angu la rJS e2e w/ C I

3時間後…

3 hours later…

ʕº ºʔoo0(…)

Angu la rJS e2e w/ C I

ʕº ºʔoo0(…)

ʕº ºʔoo0(さて、日報書いて帰りますか…)

Angu la rJS e2e w/ C I

ʕº ºʔoo0(…)

ʕº ºʔoo0(さて、日報書いて帰りますか…)

(再掲)

Angu la rJS e2e w/ C I

解決策

q.e.d.

・複数のテストタスクをgulpに登録する

=> 違うテストタスクとして起動させる

- ブラウザが起動し直される

- ちゃんとCookie削除されます

Angu la rJS e2e w/ C I

Angu la rJS e2e w/ C I

// circle.yml!!test:! override:!

!# Goのテスト! - gvm use stable; ginkgo -tags=tests -r . -cover!

!# メインのe2e! - gulp e2e! - sleep 2!

!# ログインページ その1! - gulp e2e:login_success! - sleep 2!

!# ログインページ その2! - gulp e2e:login_error1! - sleep 2!

!# ログインページ その3! - gulp e2e:login_error2

Angu la rJS e2e w/ C I

(e2e失敗のデモ)

t l ; d r

ʕº ºʔ<「TypeScriptの安心感」 // 全然触れてない

ʕº ºʔ<「テストコードの安心感」

ʕº ºʔ<「CIの安心感」

t l ; d r

第一部 完

fin.

「世界で愛されるサービス」を一緒に作りませんか?

セグメントNo1 サービス

グローバルビジネス

Business!

pairs Goフルスクラッチ

Engineering!

マッチングアルゴリズム

Big Data!

ハイブリッドエンジニア 大規模行動データ解析

CONFIDENTIAL!

Thank you :)!Thank you :)!