SlideShare ist ein Scribd-Unternehmen logo
1 von 37
Downloaden Sie, um offline zu lesen
「スピード」と「品質」を実現する 
PHP開発チームの取り組み 
! 
 ~AngularJS + FuelPHP + AspectMock~
何者? 
株式会社インテリジェンス 
 マーケティング企画統括部 
 サービス開発部 テクノロジーグループ 
! 
清田 馨一郎  
Twitter:@seikei1874 
【経歴】 
2002年 SIerに入社 
PGから叩き上げでPMまで経験 
大手企業の基幹システムからソーシャルゲーム開発まで幅広く経験 
  
2014年04月から、インテリジェンスへJOIN 
石抱き
何してる? 
サービス開発部 
 dots.(http://eventdots.jp/)の開発、運営 
 マーケティング部門の業務改善、見える化 
 … etc 
! 
(私の)ミッションを格好良く言うと 
 社内のデータサイロを見つけ、 
 サイロをつなぐデータパイプラインを構築し、 
 ビジネスの加速を促す
本日の内容
本日の内容 
【「スピード」と「品質」を実現する】取り組みを紹介 
その中でも、テストを中心に紹介 
PHP勉強会なので、AspectMockについて詳しく紹介 
Angularの技術的な話は。。。 
! 
ゴール 
 明日から、AspectMockが使いたくなっちゃう♥
スピードと品質
スピードと品質 
計画(スプリント・プランニング) 
実装 
ユーザに見てもらう(スプリント・レビュー)
スピードと品質 
計画(スプリント・プランニング) 
サイクルを早く回す 
実装 
ユーザに見てもらう(スプリント・レビュー)
サイクルを早く回すために 
テストは極力自動化 
定常的、同じ作業は機械に任せる 
! 
細かいスパンでデプロイ 
ユーザは、見て・使ってみないと分からない 
頻繁なデプロイが負荷にならないために自動化 
! 
技術的負債は残さない 
開発者の精神的安定
開発
開発システム 
PHP :5.5系 
MySQL :5.6系 
FuelPHP :1.7.2 
 ※ 一部システムでは、Phalcon使ったり 
! 
Font-end :AngularJS、TypeScript 
Test :PHPUnit、AspectMock、Karma、Jasmine、PhantomJS 
コミュニケーション :Slack 
ジョブ管理 :SOS JobScheduler 
ビルド、デプロイ :Grunt、Fabric 
CI :Jenkins 
構成管理 :Gitlab 
開発環境 :Vagrant 
課題管理 :OpenProject
開発フローとCI 
開発フロー 
 テストコードを書いて実装完了 
 Gitlabにマージリクエスト。レビューしてマージ 
 Jenkinsでテスト、デプロイ 
 Slackに通知して共有 
! 
CI 
 毎朝、実行 
 Slackに結果を通知
フロントサイド
FuelPHP 
FulePHPでは極力APIを作るようにする 
ビュー部分はAngularJSで作りこむ 
! 
素のController_Restでは、 
想定外エラーのレスポンスなど 
扱いヅライのでカスタマイズ
AngularJS 
多分に漏れず、DOM地獄から逃げたかった 
社内ツールではあるが、UIは今風にしたい 
! 
秘伝のjQueryソースは無くしたい 
ある程度、書き方が統一できる
TypeScript 
コンパイルを通すので、構文チェックができる。 
デバッグでエラー箇所が特定し易い 
→ 作業効率は上がる
Wijmo(ウィジモ) 
リッチUIを提供するJavaScriptライブラリ 
データグリッドやチャートなどのWidgetを多数提供 
AngularのDirectiveが標準で提供 
!
Grunt + Karma + Jasmine + PhantomJS 
Grunt 
 TypeScriptのコンパイル  
 Karmaの実行 
! 
 デプロイ、CI 
  JenkinsでGrunt実行 
! 
Jasmine 
APIレスポンスをスタブ化し、 
テストが柔軟にできる
AspectMock
AspectMock 
モック フレームワーク 
https://github.com/Codeception/AspectMock 
! 
PHPテストフレームワーク「Codeception」と同じ作者 
! 
PHPでAOPを実現する「Go-AOP」を使用して 
メソッドを差し替える仕組み 
! 
Go-AOP 
https://github.com/lisachenko/go-aop-php 
AOP:アスペクト志向プログラミング
なぜAspectMock? 
テストフルなコード??? 
! 
FuelPHPとの親和性 
Fuelは、staticを多様 
1.7.2から標準で設定済(core/bootstrap_phpunit.php) 
! 
単体テストのバリエーションが増える  
テストデータ作成に苦労しない 
異常ケースが容易にできる
設定、使い方 
達人出版会 
「はじめてのフレームワークとし 
てのFuelPHP第2版(3) 実践編」 
! 
※AspectMock使用時のクラスロードエラーの解消の部分だけでも、1000円の価値はありました。 
○ PHP Advent Calendar 2014 kenjisさんの記事 
「普通じゃないモッキングフレームワークAspectMockがパワフル過ぎる」 
  http://blog.a-way-out.net/blog/2014/12/10/aspect-mock/
Proxy 
ClassProxy 
静的メソッドのMock 
! 
InstansProxy 
インスタンスのMock 
! 
Test Doubles Builder 
ClassProxy, InstansProxyを良しなに作成してくれる 
! 
FuncProxy (>= 5.0.0) 
指定したNamespaceのファンクションをMock化。 
NativeファンクションもMock化可能!!
こんなときどうする? 
1. オブジェクトの中で呼んでいるstaticメソッド 
2. DBエラー、ネットワークエラーなどの例外 
3. 外部リソースからの取得データ 
4. 状態によって戻り値が変わるメソッド 
DEMOしながら説明します
まとめ 
• 定常、定期的な作業は積極的に自動化すべし 
• テストコードは、テスト実施と同時に書く 
• AspectMockを使えば、出来ないテストは無い(多分) 
• SpecメソッドでBDDも可能(試してません) 
• AspectMockのクセは強いので慣れましょう
ご質問ありますか? 
http://eventdots.jp/
ありがとうございました! 
http://eventdots.jp/ 
いっしょに働く仲間を募集中!!!
付録
FuelPHP 
想定外のエラー発生時も、ちゃんとレスポンスを返すようにする 
! 
 protected function response($data = array(), $message = '') {! 
! if(!array_key_exists('error_code', $data)) {! 
! ! $m_array = Arr::merge(array('error_code' => '200', 'message' => $message), array('data' => $data));! 
! }! 
! parent::response($m_array);! 
 }! 
! 
 public function router($resource, $arguments) {! 
! try {! 
$ret = parent::router($resource, $arguments);! ! 
if ($ret === false) {! 
parent::response(array('error_code' => '403', 'message' => 'Exception'), 403);! 
}! 
} catch (Exception $e) {! 
Log::error($e->getTraceAsString());! 
Log::error($e->getMessage());! ! 
parent::response(array('error_code' => '500', 'message' => 'Internal error.'), 500);! 
}! 
}! 
 }!
AspectMockを使ってみる 
こんなクラスをMock化してみる 
<?php! 
! 
namespace Sample;! 
! 
class Model_User extends Model {! 
! 
private $_id;! 
private $_name;! 
! 
public function __construct($id, $name) {! 
$this->_id = $id;! 
$this->_name = $name;! 
}! 
! 
public function getName() {! 
return $this->_name;! 
}! 
! 
public function getDate() {! 
return date('Y-m-d H:i:s');! 
}! 
} 
<?php! 
! 
namespace Sample;! 
! 
class Model_Suser extends Model {! 
! 
private static $_name = '静的な値';! 
! 
public static function getName() {! 
return static::$_name;! 
}! 
! 
public static function callPrivate() {! 
return static::privatefunc();! 
}! 
! 
private static function privatefunc() {! 
$time = FuelCoreDate::time();! 
return "private:" . $time . PHP_EOL;! 
}! 
}
AspectMockを使ってみる1 
<?php! 
use AspectMockTest as mock;! 
! 
class Test_Sample extends FuelCoreTestCase {! 
! 
protected function setUp() {! 
Autoloader::add_namespace(! 
! ! ! ‘Sample',! 
! ! ! APPPATH . 'classes' . DS . 'sample/');! 
}! 
! 
public function test_インスタンスProxyのケース() {! 
$user = new SampleModel_User(1, 'TestName');! 
! 
// Mock化! 
$mock = mock::double($user,! 
! ! ! ['getName' => 'DummyName']);! 
! 
// 指定したNamespace内であれば、標準関数もMock化できる! 
mock::func('Sample', 'date', ‘now!!');! 
! 
$name = $user->getName();! 
$data = $user->getDate();! 
! 
$mock->verifyInvokedOnce('getName');! 
$mock->verifyInvokedOnce('getDate');! 
! 
$this->assertEquals('DummyName', $name);! 
$this->assertEquals('now!!', $data);! 
}! 
} 
public function test_ClassProxyのケース() ! 
{! 
! 
mock::double('SampleModel_Suser', [! 
! ! ! ! ! 'getName' => 'Dummy']);! 
! 
$name = SampleModel_Suser::getName();! 
! 
$this->assertEquals('Dummy', $name);! 
! 
} 
インスタンスもMock化 
クラス名からMock化 
標準関数もMock化!
AspectMockを使ってみる2 
/**! 
* @expectedException FuelException! 
*/! 
public function test_例外発生ケース() {! 
例外を強制的に発生できる 
DBエラー、ネットワークエラーなど、発生させ難いテストが容易になる 
! 
mock::double('SampleModel_Suser', [! 
'getName' => function() {throw new FuelCoreFuelException("例外発生");}! 
]);! 
! 
try {! 
SampleModel_Suser::getName();! 
! 
} catch (FuelException $e) {! 
! 
$this->assertTrue(true);! 
throw $e;! 
}! 
! 
$this->assertTrue(false);! 
}!
AspectMockを使ってみる3 
public function test_Privateなメソッドのケース() {! 
privateメソッドもMock化できる 
 ※AspectMockの機能ではありませんが、Closureでprivateメソッドが直接呼べる 
! 
// privateメソッドで呼んでるクラスをMock化! 
mock::double('FuelCoreDate', ['time' => 'hogehoge']);! 
! 
// Privateメソッドを直接呼ぶ! 
Closure::bind(! 
function () {! 
$obj = new SampleModel_Suser();! 
$ret = $obj->privatefunc();! 
! 
$this->assertEquals('private:hogehoge' . PHP_EOL, $ret);! 
},! 
$this,! 
'SampleModel_Suser'! 
)->__invoke();! 
! 
mock::clean();! 
mock::double('SampleModel_Suser', ['privatefunc' => 'プライベート関数もMock化']);! 
! 
$ret = SampleModel_Suser::callPrivate();! 
$this->assertEquals('プライベート関数もMock化', $ret);! 
! 
}
AspectMockを使ってみる4 
<?php! 
namespace Sample;! 
! 
use FuelCoreDB;! 
! 
class Model_Transaction extends Model {! 
! 
public static function transaction() {! 
! 
if(!DB::in_transaction()) {! 
DB::start_transaction();! 
}! 
! 
try {! 
// DB処理! 
Model_Orm_User::find();! 
! 
DB::commit_transaction();! 
! 
} catch(FuelException $e) {! 
if(DB::in_transaction()) {! 
DB::rollback_transaction();! 
}! 
! 
throw $e;! 
}! 
}! 
}! 
【ケース】 
 例外が発生したときの挙動をテストしたい 
  
 1. トランザクションが無ければ貼る 
 2. 例外が発生しとき、トランザクションが 
   貼ってあれば、Rollbackする
AspectMockを使ってみる4 
public function test_呼び出し回数で挙動を変えるケース() ! 
{! 
! $cnt = 0;! 
! $mock = mock::double(! 
'FuelCoreDB',! 
[! 
'start_transaction' => true,! 
'commit_transaction' => true,! 
'rollback_transaction' => true,! 
'in_transaction' => function () use (&$cnt) {! 
if ($cnt == 0) {! 
$cnt++;! 
return false;! 
} elseif ($cnt == 1) {! 
$cnt++;! 
return true;! 
} else {! 
$cnt++;! 
return __AM_CONTINUE__; // オリジナルの処理がされる! 
}! 
}]);! 
! 
mock::double('SampleModel_Orm_User', [! 
! ! ! 'find' => function() {! 
! ! ! ! throw new FuelException(“Exception強制発生");}! 
! ! ]);! 
! 
try {! 
SampleModel_Transaction::transaction();! 
} catch(FuelException $e) {! 
$mock->verifyInvokedOnce('start_transaction');! 
$mock->verifyNeverInvoked('commit_transaction');! 
$mock->verifyInvokedMultipleTimes('in_transaction', 2);! 
$mock->verifyInvokedOnce('rollback_transaction');! 
}! 
} 
無名関数の引数に 
変数を参照渡しして 
呼び出し回数を 
カウント
AspectMockを使ってみる5 
【ケース】 
ORMでfindした値が、 
想定どおりの処理が行われた値で 
saveされるかを確認したい 
<?php! 
! 
namespace Sample;! 
! 
class Model_Orm_User extends OrmModel! 
{! 
! 
<?php! 
! 
namespace Sample;! 
! 
class Model_Update extends Model! 
{! 
public static function update()! 
{! 
$model = Model_Orm_User::find();! 
! 
foreach($model as $ret) {! 
$ret['val'] = 'hugehuge';! 
$ret->save();! 
}! 
}! 
}! 
protected static $_properties = [! 
'id',! 
'val',! 
];! 
}!
AspectMockを使ってみる5 
確認したいORMクラ 
スを継承してfindし 
た結果をダミー値で 
定義。 
! 
saveの引数を無名関 
数でチェック 
<?php! 
Autoloader::add_namespace(‘Sample',! 
! ! ! ! APPPATH . 'classes' . DS . 'sample/');! ! 
class Tests_Model_StubModel extends SampleModel_Orm_User {! ! 
protected $_data = [‘id','val'];! ! 
function __construct($id, $val){! 
$this->_data['id'] = $id;! 
$this->_data['val'] = $val;! 
}! 
} 
public function test_ORMでの更新値チェックのケース() {! 
// ORMでDBから値を取得して処理して更新するパターン! 
$modify_data = 'hugehuge';! ! 
$data = new Tests_Model_StubModel('1', 'hogehoge');! ! 
mock::double('SampleModel_Orm_User',! 
[! 
'find' => function ($param) use ($data) {! 
// ORMのfindは、レコード単位のORMクラス配列が返る! 
return [$data];! 
}! 
]);! ! 
mock::double('OrmModel',[! 
'save' => function() use ($modify_data) {! 
FuelCoreTestCase::assertEquals(! 
! ! ! $this->_data['val'], $modify_data! 
! ! );! 
}! 
]);! ! 
SampleModel_Update::update();! 
}!

Weitere ähnliche Inhalte

Was ist angesagt?

WordPressと外部APIとの連携
WordPressと外部APIとの連携WordPressと外部APIとの連携
WordPressと外部APIとの連携Hidekazu Ishikawa
 
仕事の手離れを良くする手段としての、静的検査のあるテンプレートエンジン (YATT::Lite talk at 2014 テンプレートエンジンNight)
仕事の手離れを良くする手段としての、静的検査のあるテンプレートエンジン (YATT::Lite talk at 2014 テンプレートエンジンNight)仕事の手離れを良くする手段としての、静的検査のあるテンプレートエンジン (YATT::Lite talk at 2014 テンプレートエンジンNight)
仕事の手離れを良くする手段としての、静的検査のあるテンプレートエンジン (YATT::Lite talk at 2014 テンプレートエンジンNight)Hiroaki KOBAYASHI
 
【アシアル塾】PHPオブジェクト指向再入門・第一回クラスとオブジェクト
【アシアル塾】PHPオブジェクト指向再入門・第一回クラスとオブジェクト【アシアル塾】PHPオブジェクト指向再入門・第一回クラスとオブジェクト
【アシアル塾】PHPオブジェクト指向再入門・第一回クラスとオブジェクトYuki Okamoto
 
40分濃縮 PHP classの教室
40分濃縮 PHP classの教室40分濃縮 PHP classの教室
40分濃縮 PHP classの教室Yusuke Ando
 
Gen-Template-for-Perl
Gen-Template-for-PerlGen-Template-for-Perl
Gen-Template-for-Perlnasneg
 
俺のフックがこんなに簡単なわけがない。
俺のフックがこんなに簡単なわけがない。俺のフックがこんなに簡単なわけがない。
俺のフックがこんなに簡単なわけがない。Hishikawa Takuro
 
Perlで伝統芸能
Perlで伝統芸能Perlで伝統芸能
Perlで伝統芸能hitode909
 
ビギナーだから使いたいO/Rマッパー ~Tengを使った開発~
ビギナーだから使いたいO/Rマッパー ~Tengを使った開発~ビギナーだから使いたいO/Rマッパー ~Tengを使った開発~
ビギナーだから使いたいO/Rマッパー ~Tengを使った開発~Akabane Hiroyuki
 
GMO TECHNOLOGY BOOT CAMP2015(PHP編)
GMO TECHNOLOGY BOOT CAMP2015(PHP編)GMO TECHNOLOGY BOOT CAMP2015(PHP編)
GMO TECHNOLOGY BOOT CAMP2015(PHP編)Arata Fujimura
 
よいことも悪いこともぜんぶPHPが教えてくれた
よいことも悪いこともぜんぶPHPが教えてくれたよいことも悪いこともぜんぶPHPが教えてくれた
よいことも悪いこともぜんぶPHPが教えてくれたMoriyoshi Koizumi
 
20150207コデアルエンジニア学生向けハッカソン就活イベント発表資料
20150207コデアルエンジニア学生向けハッカソン就活イベント発表資料20150207コデアルエンジニア学生向けハッカソン就活イベント発表資料
20150207コデアルエンジニア学生向けハッカソン就活イベント発表資料codeal
 
実用裏方 Perl 入門
実用裏方 Perl 入門実用裏方 Perl 入門
実用裏方 Perl 入門keroyonn
 

Was ist angesagt? (18)

WordPressと外部APIとの連携
WordPressと外部APIとの連携WordPressと外部APIとの連携
WordPressと外部APIとの連携
 
仕事の手離れを良くする手段としての、静的検査のあるテンプレートエンジン (YATT::Lite talk at 2014 テンプレートエンジンNight)
仕事の手離れを良くする手段としての、静的検査のあるテンプレートエンジン (YATT::Lite talk at 2014 テンプレートエンジンNight)仕事の手離れを良くする手段としての、静的検査のあるテンプレートエンジン (YATT::Lite talk at 2014 テンプレートエンジンNight)
仕事の手離れを良くする手段としての、静的検査のあるテンプレートエンジン (YATT::Lite talk at 2014 テンプレートエンジンNight)
 
【アシアル塾】PHPオブジェクト指向再入門・第一回クラスとオブジェクト
【アシアル塾】PHPオブジェクト指向再入門・第一回クラスとオブジェクト【アシアル塾】PHPオブジェクト指向再入門・第一回クラスとオブジェクト
【アシアル塾】PHPオブジェクト指向再入門・第一回クラスとオブジェクト
 
40分濃縮 PHP classの教室
40分濃縮 PHP classの教室40分濃縮 PHP classの教室
40分濃縮 PHP classの教室
 
Gen-Template-for-Perl
Gen-Template-for-PerlGen-Template-for-Perl
Gen-Template-for-Perl
 
俺のフックがこんなに簡単なわけがない。
俺のフックがこんなに簡単なわけがない。俺のフックがこんなに簡単なわけがない。
俺のフックがこんなに簡単なわけがない。
 
Perlで伝統芸能
Perlで伝統芸能Perlで伝統芸能
Perlで伝統芸能
 
Ci tutorial
Ci tutorialCi tutorial
Ci tutorial
 
ビギナーだから使いたいO/Rマッパー ~Tengを使った開発~
ビギナーだから使いたいO/Rマッパー ~Tengを使った開発~ビギナーだから使いたいO/Rマッパー ~Tengを使った開発~
ビギナーだから使いたいO/Rマッパー ~Tengを使った開発~
 
GMO TECHNOLOGY BOOT CAMP2015(PHP編)
GMO TECHNOLOGY BOOT CAMP2015(PHP編)GMO TECHNOLOGY BOOT CAMP2015(PHP編)
GMO TECHNOLOGY BOOT CAMP2015(PHP編)
 
Try Jetpack
Try JetpackTry Jetpack
Try Jetpack
 
よいことも悪いこともぜんぶPHPが教えてくれた
よいことも悪いこともぜんぶPHPが教えてくれたよいことも悪いこともぜんぶPHPが教えてくれた
よいことも悪いこともぜんぶPHPが教えてくれた
 
あらためてPHP5.3
あらためてPHP5.3あらためてPHP5.3
あらためてPHP5.3
 
20150207コデアルエンジニア学生向けハッカソン就活イベント発表資料
20150207コデアルエンジニア学生向けハッカソン就活イベント発表資料20150207コデアルエンジニア学生向けハッカソン就活イベント発表資料
20150207コデアルエンジニア学生向けハッカソン就活イベント発表資料
 
MT meets PHP
MT meets PHPMT meets PHP
MT meets PHP
 
実用裏方 Perl 入門
実用裏方 Perl 入門実用裏方 Perl 入門
実用裏方 Perl 入門
 
test
testtest
test
 
Django boodoo
Django boodooDjango boodoo
Django boodoo
 

Andere mochten auch

Behatで行う、E2Eテスト入門
Behatで行う、E2Eテスト入門Behatで行う、E2Eテスト入門
Behatで行う、E2Eテスト入門leverages_event
 
今、最もイケてるPHPフレームワークLaravel4
今、最もイケてるPHPフレームワークLaravel4今、最もイケてるPHPフレームワークLaravel4
今、最もイケてるPHPフレームワークLaravel4アシアル株式会社
 
AspectMock 最強のモッキングフレームワーク
AspectMock 最強のモッキングフレームワークAspectMock 最強のモッキングフレームワーク
AspectMock 最強のモッキングフレームワークkenjis
 
Behat+Symfony2ではじめるBDD超入門
Behat+Symfony2ではじめるBDD超入門Behat+Symfony2ではじめるBDD超入門
Behat+Symfony2ではじめるBDD超入門晃 遠山
 
Yet another use of Phalcon
Yet another use of PhalconYet another use of Phalcon
Yet another use of PhalconYuji Iwai
 
PHP buildpackでhackとphalconが動いた件について
PHP buildpackでhackとphalconが動いた件についてPHP buildpackでhackとphalconが動いた件について
PHP buildpackでhackとphalconが動いた件について健治郎 安井
 
3流プログラマーから見たPhalconとWISP
3流プログラマーから見たPhalconとWISP3流プログラマーから見たPhalconとWISP
3流プログラマーから見たPhalconとWISPYamaYamamoto
 
Phalcon勉強会資料
Phalcon勉強会資料Phalcon勉強会資料
Phalcon勉強会資料Yuji Otani
 
Phalcon + AngularJSで作る動画プラットフォーム
Phalcon + AngularJSで作る動画プラットフォームPhalcon + AngularJSで作る動画プラットフォーム
Phalcon + AngularJSで作る動画プラットフォームryo-utsunomiya
 
PHP Codeception テスト -- 日本語
PHP Codeception テスト -- 日本語PHP Codeception テスト -- 日本語
PHP Codeception テスト -- 日本語Florent Batard
 
「Selenium実践入門」で学ぶテスト自動化の世界
「Selenium実践入門」で学ぶテスト自動化の世界「Selenium実践入門」で学ぶテスト自動化の世界
「Selenium実践入門」で学ぶテスト自動化の世界Nozomi Ito
 
大規模ソーシャルゲームを支える技術~PHP+MySQLを使った高負荷対策~
大規模ソーシャルゲームを支える技術~PHP+MySQLを使った高負荷対策~大規模ソーシャルゲームを支える技術~PHP+MySQLを使った高負荷対策~
大規模ソーシャルゲームを支える技術~PHP+MySQLを使った高負荷対策~infinite_loop
 
【17-E-1】自動化はどこに向かうのか~まだ開発・運用の自動化で消耗しているの?~
【17-E-1】自動化はどこに向かうのか~まだ開発・運用の自動化で消耗しているの?~【17-E-1】自動化はどこに向かうのか~まだ開発・運用の自動化で消耗しているの?~
【17-E-1】自動化はどこに向かうのか~まだ開発・運用の自動化で消耗しているの?~Masahito Zembutsu
 
Laravel 5.1 LTSでサービスを作る
Laravel 5.1 LTSでサービスを作るLaravel 5.1 LTSでサービスを作る
Laravel 5.1 LTSでサービスを作るinfinite_loop
 
今、最もイケてるPHPフレームワークLaravel4
今、最もイケてるPHPフレームワークLaravel4今、最もイケてるPHPフレームワークLaravel4
今、最もイケてるPHPフレームワークLaravel4leverages_event
 
デザイナさんにGithubでpr投げてもらうまで
デザイナさんにGithubでpr投げてもらうまでデザイナさんにGithubでpr投げてもらうまで
デザイナさんにGithubでpr投げてもらうまでHideharu Okuma
 
AngularJSでの非同期処理の話
AngularJSでの非同期処理の話AngularJSでの非同期処理の話
AngularJSでの非同期処理の話Yosuke Onoue
 

Andere mochten auch (20)

Behatで行う、E2Eテスト入門
Behatで行う、E2Eテスト入門Behatで行う、E2Eテスト入門
Behatで行う、E2Eテスト入門
 
今、最もイケてるPHPフレームワークLaravel4
今、最もイケてるPHPフレームワークLaravel4今、最もイケてるPHPフレームワークLaravel4
今、最もイケてるPHPフレームワークLaravel4
 
AspectMock 最強のモッキングフレームワーク
AspectMock 最強のモッキングフレームワークAspectMock 最強のモッキングフレームワーク
AspectMock 最強のモッキングフレームワーク
 
behatで始めるBDD
behatで始めるBDDbehatで始めるBDD
behatで始めるBDD
 
Behat+Symfony2ではじめるBDD超入門
Behat+Symfony2ではじめるBDD超入門Behat+Symfony2ではじめるBDD超入門
Behat+Symfony2ではじめるBDD超入門
 
Yet another use of Phalcon
Yet another use of PhalconYet another use of Phalcon
Yet another use of Phalcon
 
PHP buildpackでhackとphalconが動いた件について
PHP buildpackでhackとphalconが動いた件についてPHP buildpackでhackとphalconが動いた件について
PHP buildpackでhackとphalconが動いた件について
 
受託開発のPhalcon
受託開発のPhalcon受託開発のPhalcon
受託開発のPhalcon
 
3流プログラマーから見たPhalconとWISP
3流プログラマーから見たPhalconとWISP3流プログラマーから見たPhalconとWISP
3流プログラマーから見たPhalconとWISP
 
Phalcon勉強会資料
Phalcon勉強会資料Phalcon勉強会資料
Phalcon勉強会資料
 
Phalcon + AngularJSで作る動画プラットフォーム
Phalcon + AngularJSで作る動画プラットフォームPhalcon + AngularJSで作る動画プラットフォーム
Phalcon + AngularJSで作る動画プラットフォーム
 
PHP Codeception テスト -- 日本語
PHP Codeception テスト -- 日本語PHP Codeception テスト -- 日本語
PHP Codeception テスト -- 日本語
 
「Selenium実践入門」で学ぶテスト自動化の世界
「Selenium実践入門」で学ぶテスト自動化の世界「Selenium実践入門」で学ぶテスト自動化の世界
「Selenium実践入門」で学ぶテスト自動化の世界
 
大規模ソーシャルゲームを支える技術~PHP+MySQLを使った高負荷対策~
大規模ソーシャルゲームを支える技術~PHP+MySQLを使った高負荷対策~大規模ソーシャルゲームを支える技術~PHP+MySQLを使った高負荷対策~
大規模ソーシャルゲームを支える技術~PHP+MySQLを使った高負荷対策~
 
【17-E-1】自動化はどこに向かうのか~まだ開発・運用の自動化で消耗しているの?~
【17-E-1】自動化はどこに向かうのか~まだ開発・運用の自動化で消耗しているの?~【17-E-1】自動化はどこに向かうのか~まだ開発・運用の自動化で消耗しているの?~
【17-E-1】自動化はどこに向かうのか~まだ開発・運用の自動化で消耗しているの?~
 
Laravel 5.1 LTSでサービスを作る
Laravel 5.1 LTSでサービスを作るLaravel 5.1 LTSでサービスを作る
Laravel 5.1 LTSでサービスを作る
 
今、最もイケてるPHPフレームワークLaravel4
今、最もイケてるPHPフレームワークLaravel4今、最もイケてるPHPフレームワークLaravel4
今、最もイケてるPHPフレームワークLaravel4
 
Mock
MockMock
Mock
 
デザイナさんにGithubでpr投げてもらうまで
デザイナさんにGithubでpr投げてもらうまでデザイナさんにGithubでpr投げてもらうまで
デザイナさんにGithubでpr投げてもらうまで
 
AngularJSでの非同期処理の話
AngularJSでの非同期処理の話AngularJSでの非同期処理の話
AngularJSでの非同期処理の話
 

Ähnlich wie 「スピード」と「品質」を実現するPHP開発チームの取り組み~AngularJS+FuelPHP+AspectMock~

MT meets PHP - PHP conference Kansai 2013
MT meets PHP - PHP conference Kansai 2013MT meets PHP - PHP conference Kansai 2013
MT meets PHP - PHP conference Kansai 2013純生 野田
 
Data api workshop at Co-Edo
Data api workshop at Co-EdoData api workshop at Co-Edo
Data api workshop at Co-EdoYuji Takayama
 
勉強会force#4 Chatter Integration
勉強会force#4 Chatter Integration勉強会force#4 Chatter Integration
勉強会force#4 Chatter IntegrationKazuki Nakajima
 
MTで学ぶセキュアプログラミング@MT Tokyo
MTで学ぶセキュアプログラミング@MT TokyoMTで学ぶセキュアプログラミング@MT Tokyo
MTで学ぶセキュアプログラミング@MT Tokyo純生 野田
 
ソーシャルアプリ勉強会(第一回資料)配布用
ソーシャルアプリ勉強会(第一回資料)配布用ソーシャルアプリ勉強会(第一回資料)配布用
ソーシャルアプリ勉強会(第一回資料)配布用Yatabe Terumasa
 
PHPコードではなく PHPコードの「書き方」を知る
PHPコードではなく PHPコードの「書き方」を知るPHPコードではなく PHPコードの「書き方」を知る
PHPコードではなく PHPコードの「書き方」を知るMasashi Shinbara
 
速くなければスマフォじゃない - インターンバージョン-
速くなければスマフォじゃない - インターンバージョン-速くなければスマフォじゃない - インターンバージョン-
速くなければスマフォじゃない - インターンバージョン-Kazunari Hara
 
WordPressで提供するWeb API
WordPressで提供するWeb APIWordPressで提供するWeb API
WordPressで提供するWeb APIYuko Toriyama
 
広告配信現場で使うSpark機械学習
広告配信現場で使うSpark機械学習広告配信現場で使うSpark機械学習
広告配信現場で使うSpark機械学習x1 ichi
 
Hack/HHVM 入門
Hack/HHVM 入門Hack/HHVM 入門
Hack/HHVM 入門y-uti
 
初めての Data api cms どうでしょう - 大阪夏の陣
初めての Data api   cms どうでしょう - 大阪夏の陣初めての Data api   cms どうでしょう - 大阪夏の陣
初めての Data api cms どうでしょう - 大阪夏の陣Yuji Takayama
 
コンテナで作れるFaaS
コンテナで作れるFaaSコンテナで作れるFaaS
コンテナで作れるFaaS真吾 吉田
 
初めての Data api
初めての Data api初めての Data api
初めての Data apiYuji Takayama
 
⑲jQueryをおぼえよう!その5
⑲jQueryをおぼえよう!その5⑲jQueryをおぼえよう!その5
⑲jQueryをおぼえよう!その5Nishida Kansuke
 
メタな感じのプログラミング(プロ生 + わんくま 071118)
メタな感じのプログラミング(プロ生 + わんくま 071118)メタな感じのプログラミング(プロ生 + わんくま 071118)
メタな感じのプログラミング(プロ生 + わんくま 071118)Tatsuya Ishikawa
 
[東京] JapanSharePointGroup 勉強会 #2
[東京] JapanSharePointGroup 勉強会 #2[東京] JapanSharePointGroup 勉強会 #2
[東京] JapanSharePointGroup 勉強会 #2Atsuo Yamasaki
 
プログラマ講習第2回
プログラマ講習第2回プログラマ講習第2回
プログラマ講習第2回Yuma Yoshimoto
 

Ähnlich wie 「スピード」と「品質」を実現するPHP開発チームの取り組み~AngularJS+FuelPHP+AspectMock~ (20)

MT meets PHP - PHP conference Kansai 2013
MT meets PHP - PHP conference Kansai 2013MT meets PHP - PHP conference Kansai 2013
MT meets PHP - PHP conference Kansai 2013
 
Data api workshop at Co-Edo
Data api workshop at Co-EdoData api workshop at Co-Edo
Data api workshop at Co-Edo
 
勉強会force#4 Chatter Integration
勉強会force#4 Chatter Integration勉強会force#4 Chatter Integration
勉強会force#4 Chatter Integration
 
MTで学ぶセキュアプログラミング@MT Tokyo
MTで学ぶセキュアプログラミング@MT TokyoMTで学ぶセキュアプログラミング@MT Tokyo
MTで学ぶセキュアプログラミング@MT Tokyo
 
Visualforce + jQuery
Visualforce + jQueryVisualforce + jQuery
Visualforce + jQuery
 
ソーシャルアプリ勉強会(第一回資料)配布用
ソーシャルアプリ勉強会(第一回資料)配布用ソーシャルアプリ勉強会(第一回資料)配布用
ソーシャルアプリ勉強会(第一回資料)配布用
 
PHPコードではなく PHPコードの「書き方」を知る
PHPコードではなく PHPコードの「書き方」を知るPHPコードではなく PHPコードの「書き方」を知る
PHPコードではなく PHPコードの「書き方」を知る
 
速くなければスマフォじゃない - インターンバージョン-
速くなければスマフォじゃない - インターンバージョン-速くなければスマフォじゃない - インターンバージョン-
速くなければスマフォじゃない - インターンバージョン-
 
WordPressで提供するWeb API
WordPressで提供するWeb APIWordPressで提供するWeb API
WordPressで提供するWeb API
 
広告配信現場で使うSpark機械学習
広告配信現場で使うSpark機械学習広告配信現場で使うSpark機械学習
広告配信現場で使うSpark機械学習
 
Cakephp api
Cakephp apiCakephp api
Cakephp api
 
Hack/HHVM 入門
Hack/HHVM 入門Hack/HHVM 入門
Hack/HHVM 入門
 
Using Dancer
Using DancerUsing Dancer
Using Dancer
 
初めての Data api cms どうでしょう - 大阪夏の陣
初めての Data api   cms どうでしょう - 大阪夏の陣初めての Data api   cms どうでしょう - 大阪夏の陣
初めての Data api cms どうでしょう - 大阪夏の陣
 
コンテナで作れるFaaS
コンテナで作れるFaaSコンテナで作れるFaaS
コンテナで作れるFaaS
 
初めての Data api
初めての Data api初めての Data api
初めての Data api
 
⑲jQueryをおぼえよう!その5
⑲jQueryをおぼえよう!その5⑲jQueryをおぼえよう!その5
⑲jQueryをおぼえよう!その5
 
メタな感じのプログラミング(プロ生 + わんくま 071118)
メタな感じのプログラミング(プロ生 + わんくま 071118)メタな感じのプログラミング(プロ生 + わんくま 071118)
メタな感じのプログラミング(プロ生 + わんくま 071118)
 
[東京] JapanSharePointGroup 勉強会 #2
[東京] JapanSharePointGroup 勉強会 #2[東京] JapanSharePointGroup 勉強会 #2
[東京] JapanSharePointGroup 勉強会 #2
 
プログラマ講習第2回
プログラマ講習第2回プログラマ講習第2回
プログラマ講習第2回
 

Mehr von leverages_event

20171206 tsumugu4 人工知能特集_v1.00_抜粋
20171206 tsumugu4 人工知能特集_v1.00_抜粋20171206 tsumugu4 人工知能特集_v1.00_抜粋
20171206 tsumugu4 人工知能特集_v1.00_抜粋leverages_event
 
【ヒカ☆ラボ】アーキテクト養成講座「入門編」 折田 武己 氏 登壇資料 20171205
【ヒカ☆ラボ】アーキテクト養成講座「入門編」 折田 武己 氏 登壇資料 20171205【ヒカ☆ラボ】アーキテクト養成講座「入門編」 折田 武己 氏 登壇資料 20171205
【ヒカ☆ラボ】アーキテクト養成講座「入門編」 折田 武己 氏 登壇資料 20171205leverages_event
 
【ヒカ☆ラボ】 dely株式会社 梅森 翔氏 登壇資料 20171121
【ヒカ☆ラボ】 dely株式会社 梅森 翔氏 登壇資料 20171121【ヒカ☆ラボ】 dely株式会社 梅森 翔氏 登壇資料 20171121
【ヒカ☆ラボ】 dely株式会社 梅森 翔氏 登壇資料 20171121leverages_event
 
【ヒカ☆ラボ】 dely株式会社 大竹 雅登氏 登壇資料 20171121
【ヒカ☆ラボ】 dely株式会社 大竹 雅登氏 登壇資料 20171121【ヒカ☆ラボ】 dely株式会社 大竹 雅登氏 登壇資料 20171121
【ヒカ☆ラボ】 dely株式会社 大竹 雅登氏 登壇資料 20171121leverages_event
 
【ヒカ☆ラボ】 dely株式会社 深尾 もとのぶ氏 登壇資料 20171121
【ヒカ☆ラボ】 dely株式会社 深尾 もとのぶ氏 登壇資料 20171121【ヒカ☆ラボ】 dely株式会社 深尾 もとのぶ氏 登壇資料 20171121
【ヒカ☆ラボ】 dely株式会社 深尾 もとのぶ氏 登壇資料 20171121leverages_event
 
【ヒカ☆ラボ】 dely株式会社 三笠 斉輝氏 登壇資料 20171121
【ヒカ☆ラボ】 dely株式会社 三笠 斉輝氏 登壇資料 20171121【ヒカ☆ラボ】 dely株式会社 三笠 斉輝氏 登壇資料 20171121
【ヒカ☆ラボ】 dely株式会社 三笠 斉輝氏 登壇資料 20171121leverages_event
 
【ヒカ☆ラボ】 株式会社AMG Solution 山口 博史氏登壇資料 20170817
【ヒカ☆ラボ】 株式会社AMG Solution 山口 博史氏登壇資料 20170817【ヒカ☆ラボ】 株式会社AMG Solution 山口 博史氏登壇資料 20170817
【ヒカ☆ラボ】 株式会社AMG Solution 山口 博史氏登壇資料 20170817leverages_event
 
【ヒカ☆ラボ】株式会社ライナフ 登壇資料
【ヒカ☆ラボ】株式会社ライナフ 登壇資料【ヒカ☆ラボ】株式会社ライナフ 登壇資料
【ヒカ☆ラボ】株式会社ライナフ 登壇資料leverages_event
 
【ヒカ☆ラボ】株式会社エアー様~ETLツール活用法について~
【ヒカ☆ラボ】株式会社エアー様~ETLツール活用法について~【ヒカ☆ラボ】株式会社エアー様~ETLツール活用法について~
【ヒカ☆ラボ】株式会社エアー様~ETLツール活用法について~leverages_event
 
インスタグラムを活用した、マーケティングについて
インスタグラムを活用した、マーケティングについてインスタグラムを活用した、マーケティングについて
インスタグラムを活用した、マーケティングについてleverages_event
 
ヒカ☆ラボ ユニティ・テクノロジーズ・ジャパン合同会社 安原 祐二氏登壇資料_20170119
ヒカ☆ラボ ユニティ・テクノロジーズ・ジャパン合同会社 安原 祐二氏登壇資料_20170119ヒカ☆ラボ ユニティ・テクノロジーズ・ジャパン合同会社 安原 祐二氏登壇資料_20170119
ヒカ☆ラボ ユニティ・テクノロジーズ・ジャパン合同会社 安原 祐二氏登壇資料_20170119leverages_event
 
ヒカ☆ラボ 株式会社PR TIMES 山田 和広氏登壇資料 20161129
ヒカ☆ラボ 株式会社PR TIMES 山田 和広氏登壇資料 20161129ヒカ☆ラボ 株式会社PR TIMES 山田 和広氏登壇資料 20161129
ヒカ☆ラボ 株式会社PR TIMES 山田 和広氏登壇資料 20161129leverages_event
 
ヒカ☆ラボ 株式会社PR TIMES 新井 隆士氏登壇資料 20161129
ヒカ☆ラボ 株式会社PR TIMES 新井 隆士氏登壇資料 20161129ヒカ☆ラボ 株式会社PR TIMES 新井 隆士氏登壇資料 20161129
ヒカ☆ラボ 株式会社PR TIMES 新井 隆士氏登壇資料 20161129leverages_event
 
ヒカラボ「自社サービス開発会社で活躍し続けるために必要な○○とは?」開発エンジニア
ヒカラボ「自社サービス開発会社で活躍し続けるために必要な○○とは?」開発エンジニア ヒカラボ「自社サービス開発会社で活躍し続けるために必要な○○とは?」開発エンジニア
ヒカラボ「自社サービス開発会社で活躍し続けるために必要な○○とは?」開発エンジニア leverages_event
 
ヒカラボ「自社サービス開発会社で活躍し続けるために必要な○○とは?」UIデザイナー
ヒカラボ「自社サービス開発会社で活躍し続けるために必要な○○とは?」UIデザイナー ヒカラボ「自社サービス開発会社で活躍し続けるために必要な○○とは?」UIデザイナー
ヒカラボ「自社サービス開発会社で活躍し続けるために必要な○○とは?」UIデザイナー leverages_event
 
初心者向けGo言語勉強会
初心者向けGo言語勉強会初心者向けGo言語勉強会
初心者向けGo言語勉強会leverages_event
 
DMM.comラボはなぜSparkを採用したのか?レコメンドエンジン開発の裏側をお話します!
DMM.comラボはなぜSparkを採用したのか?レコメンドエンジン開発の裏側をお話します!DMM.comラボはなぜSparkを採用したのか?レコメンドエンジン開発の裏側をお話します!
DMM.comラボはなぜSparkを採用したのか?レコメンドエンジン開発の裏側をお話します!leverages_event
 
Node.js×mongo dbで3年間サービス運用してみた話
Node.js×mongo dbで3年間サービス運用してみた話Node.js×mongo dbで3年間サービス運用してみた話
Node.js×mongo dbで3年間サービス運用してみた話leverages_event
 
株式会社waja 安藤様 登壇資料
株式会社waja 安藤様 登壇資料株式会社waja 安藤様 登壇資料
株式会社waja 安藤様 登壇資料leverages_event
 

Mehr von leverages_event (20)

Ac tsumugu 20170712
Ac tsumugu 20170712Ac tsumugu 20170712
Ac tsumugu 20170712
 
20171206 tsumugu4 人工知能特集_v1.00_抜粋
20171206 tsumugu4 人工知能特集_v1.00_抜粋20171206 tsumugu4 人工知能特集_v1.00_抜粋
20171206 tsumugu4 人工知能特集_v1.00_抜粋
 
【ヒカ☆ラボ】アーキテクト養成講座「入門編」 折田 武己 氏 登壇資料 20171205
【ヒカ☆ラボ】アーキテクト養成講座「入門編」 折田 武己 氏 登壇資料 20171205【ヒカ☆ラボ】アーキテクト養成講座「入門編」 折田 武己 氏 登壇資料 20171205
【ヒカ☆ラボ】アーキテクト養成講座「入門編」 折田 武己 氏 登壇資料 20171205
 
【ヒカ☆ラボ】 dely株式会社 梅森 翔氏 登壇資料 20171121
【ヒカ☆ラボ】 dely株式会社 梅森 翔氏 登壇資料 20171121【ヒカ☆ラボ】 dely株式会社 梅森 翔氏 登壇資料 20171121
【ヒカ☆ラボ】 dely株式会社 梅森 翔氏 登壇資料 20171121
 
【ヒカ☆ラボ】 dely株式会社 大竹 雅登氏 登壇資料 20171121
【ヒカ☆ラボ】 dely株式会社 大竹 雅登氏 登壇資料 20171121【ヒカ☆ラボ】 dely株式会社 大竹 雅登氏 登壇資料 20171121
【ヒカ☆ラボ】 dely株式会社 大竹 雅登氏 登壇資料 20171121
 
【ヒカ☆ラボ】 dely株式会社 深尾 もとのぶ氏 登壇資料 20171121
【ヒカ☆ラボ】 dely株式会社 深尾 もとのぶ氏 登壇資料 20171121【ヒカ☆ラボ】 dely株式会社 深尾 もとのぶ氏 登壇資料 20171121
【ヒカ☆ラボ】 dely株式会社 深尾 もとのぶ氏 登壇資料 20171121
 
【ヒカ☆ラボ】 dely株式会社 三笠 斉輝氏 登壇資料 20171121
【ヒカ☆ラボ】 dely株式会社 三笠 斉輝氏 登壇資料 20171121【ヒカ☆ラボ】 dely株式会社 三笠 斉輝氏 登壇資料 20171121
【ヒカ☆ラボ】 dely株式会社 三笠 斉輝氏 登壇資料 20171121
 
【ヒカ☆ラボ】 株式会社AMG Solution 山口 博史氏登壇資料 20170817
【ヒカ☆ラボ】 株式会社AMG Solution 山口 博史氏登壇資料 20170817【ヒカ☆ラボ】 株式会社AMG Solution 山口 博史氏登壇資料 20170817
【ヒカ☆ラボ】 株式会社AMG Solution 山口 博史氏登壇資料 20170817
 
【ヒカ☆ラボ】株式会社ライナフ 登壇資料
【ヒカ☆ラボ】株式会社ライナフ 登壇資料【ヒカ☆ラボ】株式会社ライナフ 登壇資料
【ヒカ☆ラボ】株式会社ライナフ 登壇資料
 
【ヒカ☆ラボ】株式会社エアー様~ETLツール活用法について~
【ヒカ☆ラボ】株式会社エアー様~ETLツール活用法について~【ヒカ☆ラボ】株式会社エアー様~ETLツール活用法について~
【ヒカ☆ラボ】株式会社エアー様~ETLツール活用法について~
 
インスタグラムを活用した、マーケティングについて
インスタグラムを活用した、マーケティングについてインスタグラムを活用した、マーケティングについて
インスタグラムを活用した、マーケティングについて
 
ヒカ☆ラボ ユニティ・テクノロジーズ・ジャパン合同会社 安原 祐二氏登壇資料_20170119
ヒカ☆ラボ ユニティ・テクノロジーズ・ジャパン合同会社 安原 祐二氏登壇資料_20170119ヒカ☆ラボ ユニティ・テクノロジーズ・ジャパン合同会社 安原 祐二氏登壇資料_20170119
ヒカ☆ラボ ユニティ・テクノロジーズ・ジャパン合同会社 安原 祐二氏登壇資料_20170119
 
ヒカ☆ラボ 株式会社PR TIMES 山田 和広氏登壇資料 20161129
ヒカ☆ラボ 株式会社PR TIMES 山田 和広氏登壇資料 20161129ヒカ☆ラボ 株式会社PR TIMES 山田 和広氏登壇資料 20161129
ヒカ☆ラボ 株式会社PR TIMES 山田 和広氏登壇資料 20161129
 
ヒカ☆ラボ 株式会社PR TIMES 新井 隆士氏登壇資料 20161129
ヒカ☆ラボ 株式会社PR TIMES 新井 隆士氏登壇資料 20161129ヒカ☆ラボ 株式会社PR TIMES 新井 隆士氏登壇資料 20161129
ヒカ☆ラボ 株式会社PR TIMES 新井 隆士氏登壇資料 20161129
 
ヒカラボ「自社サービス開発会社で活躍し続けるために必要な○○とは?」開発エンジニア
ヒカラボ「自社サービス開発会社で活躍し続けるために必要な○○とは?」開発エンジニア ヒカラボ「自社サービス開発会社で活躍し続けるために必要な○○とは?」開発エンジニア
ヒカラボ「自社サービス開発会社で活躍し続けるために必要な○○とは?」開発エンジニア
 
ヒカラボ「自社サービス開発会社で活躍し続けるために必要な○○とは?」UIデザイナー
ヒカラボ「自社サービス開発会社で活躍し続けるために必要な○○とは?」UIデザイナー ヒカラボ「自社サービス開発会社で活躍し続けるために必要な○○とは?」UIデザイナー
ヒカラボ「自社サービス開発会社で活躍し続けるために必要な○○とは?」UIデザイナー
 
初心者向けGo言語勉強会
初心者向けGo言語勉強会初心者向けGo言語勉強会
初心者向けGo言語勉強会
 
DMM.comラボはなぜSparkを採用したのか?レコメンドエンジン開発の裏側をお話します!
DMM.comラボはなぜSparkを採用したのか?レコメンドエンジン開発の裏側をお話します!DMM.comラボはなぜSparkを採用したのか?レコメンドエンジン開発の裏側をお話します!
DMM.comラボはなぜSparkを採用したのか?レコメンドエンジン開発の裏側をお話します!
 
Node.js×mongo dbで3年間サービス運用してみた話
Node.js×mongo dbで3年間サービス運用してみた話Node.js×mongo dbで3年間サービス運用してみた話
Node.js×mongo dbで3年間サービス運用してみた話
 
株式会社waja 安藤様 登壇資料
株式会社waja 安藤様 登壇資料株式会社waja 安藤様 登壇資料
株式会社waja 安藤様 登壇資料
 

「スピード」と「品質」を実現するPHP開発チームの取り組み~AngularJS+FuelPHP+AspectMock~

  • 2. 何者? 株式会社インテリジェンス  マーケティング企画統括部  サービス開発部 テクノロジーグループ ! 清田 馨一郎  Twitter:@seikei1874 【経歴】 2002年 SIerに入社 PGから叩き上げでPMまで経験 大手企業の基幹システムからソーシャルゲーム開発まで幅広く経験   2014年04月から、インテリジェンスへJOIN 石抱き
  • 3. 何してる? サービス開発部  dots.(http://eventdots.jp/)の開発、運営  マーケティング部門の業務改善、見える化  … etc ! (私の)ミッションを格好良く言うと  社内のデータサイロを見つけ、  サイロをつなぐデータパイプラインを構築し、  ビジネスの加速を促す
  • 5. 本日の内容 【「スピード」と「品質」を実現する】取り組みを紹介 その中でも、テストを中心に紹介 PHP勉強会なので、AspectMockについて詳しく紹介 Angularの技術的な話は。。。 ! ゴール  明日から、AspectMockが使いたくなっちゃう♥
  • 7. スピードと品質 計画(スプリント・プランニング) 実装 ユーザに見てもらう(スプリント・レビュー)
  • 8. スピードと品質 計画(スプリント・プランニング) サイクルを早く回す 実装 ユーザに見てもらう(スプリント・レビュー)
  • 9. サイクルを早く回すために テストは極力自動化 定常的、同じ作業は機械に任せる ! 細かいスパンでデプロイ ユーザは、見て・使ってみないと分からない 頻繁なデプロイが負荷にならないために自動化 ! 技術的負債は残さない 開発者の精神的安定
  • 11. 開発システム PHP :5.5系 MySQL :5.6系 FuelPHP :1.7.2  ※ 一部システムでは、Phalcon使ったり ! Font-end :AngularJS、TypeScript Test :PHPUnit、AspectMock、Karma、Jasmine、PhantomJS コミュニケーション :Slack ジョブ管理 :SOS JobScheduler ビルド、デプロイ :Grunt、Fabric CI :Jenkins 構成管理 :Gitlab 開発環境 :Vagrant 課題管理 :OpenProject
  • 12. 開発フローとCI 開発フロー  テストコードを書いて実装完了  Gitlabにマージリクエスト。レビューしてマージ  Jenkinsでテスト、デプロイ  Slackに通知して共有 ! CI  毎朝、実行  Slackに結果を通知
  • 14. FuelPHP FulePHPでは極力APIを作るようにする ビュー部分はAngularJSで作りこむ ! 素のController_Restでは、 想定外エラーのレスポンスなど 扱いヅライのでカスタマイズ
  • 15. AngularJS 多分に漏れず、DOM地獄から逃げたかった 社内ツールではあるが、UIは今風にしたい ! 秘伝のjQueryソースは無くしたい ある程度、書き方が統一できる
  • 18. Grunt + Karma + Jasmine + PhantomJS Grunt  TypeScriptのコンパイル   Karmaの実行 !  デプロイ、CI   JenkinsでGrunt実行 ! Jasmine APIレスポンスをスタブ化し、 テストが柔軟にできる
  • 20. AspectMock モック フレームワーク https://github.com/Codeception/AspectMock ! PHPテストフレームワーク「Codeception」と同じ作者 ! PHPでAOPを実現する「Go-AOP」を使用して メソッドを差し替える仕組み ! Go-AOP https://github.com/lisachenko/go-aop-php AOP:アスペクト志向プログラミング
  • 21. なぜAspectMock? テストフルなコード??? ! FuelPHPとの親和性 Fuelは、staticを多様 1.7.2から標準で設定済(core/bootstrap_phpunit.php) ! 単体テストのバリエーションが増える  テストデータ作成に苦労しない 異常ケースが容易にできる
  • 22. 設定、使い方 達人出版会 「はじめてのフレームワークとし てのFuelPHP第2版(3) 実践編」 ! ※AspectMock使用時のクラスロードエラーの解消の部分だけでも、1000円の価値はありました。 ○ PHP Advent Calendar 2014 kenjisさんの記事 「普通じゃないモッキングフレームワークAspectMockがパワフル過ぎる」   http://blog.a-way-out.net/blog/2014/12/10/aspect-mock/
  • 23. Proxy ClassProxy 静的メソッドのMock ! InstansProxy インスタンスのMock ! Test Doubles Builder ClassProxy, InstansProxyを良しなに作成してくれる ! FuncProxy (>= 5.0.0) 指定したNamespaceのファンクションをMock化。 NativeファンクションもMock化可能!!
  • 24. こんなときどうする? 1. オブジェクトの中で呼んでいるstaticメソッド 2. DBエラー、ネットワークエラーなどの例外 3. 外部リソースからの取得データ 4. 状態によって戻り値が変わるメソッド DEMOしながら説明します
  • 25. まとめ • 定常、定期的な作業は積極的に自動化すべし • テストコードは、テスト実施と同時に書く • AspectMockを使えば、出来ないテストは無い(多分) • SpecメソッドでBDDも可能(試してません) • AspectMockのクセは強いので慣れましょう
  • 29. FuelPHP 想定外のエラー発生時も、ちゃんとレスポンスを返すようにする !  protected function response($data = array(), $message = '') {! ! if(!array_key_exists('error_code', $data)) {! ! ! $m_array = Arr::merge(array('error_code' => '200', 'message' => $message), array('data' => $data));! ! }! ! parent::response($m_array);!  }! !  public function router($resource, $arguments) {! ! try {! $ret = parent::router($resource, $arguments);! ! if ($ret === false) {! parent::response(array('error_code' => '403', 'message' => 'Exception'), 403);! }! } catch (Exception $e) {! Log::error($e->getTraceAsString());! Log::error($e->getMessage());! ! parent::response(array('error_code' => '500', 'message' => 'Internal error.'), 500);! }! }!  }!
  • 30. AspectMockを使ってみる こんなクラスをMock化してみる <?php! ! namespace Sample;! ! class Model_User extends Model {! ! private $_id;! private $_name;! ! public function __construct($id, $name) {! $this->_id = $id;! $this->_name = $name;! }! ! public function getName() {! return $this->_name;! }! ! public function getDate() {! return date('Y-m-d H:i:s');! }! } <?php! ! namespace Sample;! ! class Model_Suser extends Model {! ! private static $_name = '静的な値';! ! public static function getName() {! return static::$_name;! }! ! public static function callPrivate() {! return static::privatefunc();! }! ! private static function privatefunc() {! $time = FuelCoreDate::time();! return "private:" . $time . PHP_EOL;! }! }
  • 31. AspectMockを使ってみる1 <?php! use AspectMockTest as mock;! ! class Test_Sample extends FuelCoreTestCase {! ! protected function setUp() {! Autoloader::add_namespace(! ! ! ! ‘Sample',! ! ! ! APPPATH . 'classes' . DS . 'sample/');! }! ! public function test_インスタンスProxyのケース() {! $user = new SampleModel_User(1, 'TestName');! ! // Mock化! $mock = mock::double($user,! ! ! ! ['getName' => 'DummyName']);! ! // 指定したNamespace内であれば、標準関数もMock化できる! mock::func('Sample', 'date', ‘now!!');! ! $name = $user->getName();! $data = $user->getDate();! ! $mock->verifyInvokedOnce('getName');! $mock->verifyInvokedOnce('getDate');! ! $this->assertEquals('DummyName', $name);! $this->assertEquals('now!!', $data);! }! } public function test_ClassProxyのケース() ! {! ! mock::double('SampleModel_Suser', [! ! ! ! ! ! 'getName' => 'Dummy']);! ! $name = SampleModel_Suser::getName();! ! $this->assertEquals('Dummy', $name);! ! } インスタンスもMock化 クラス名からMock化 標準関数もMock化!
  • 32. AspectMockを使ってみる2 /**! * @expectedException FuelException! */! public function test_例外発生ケース() {! 例外を強制的に発生できる DBエラー、ネットワークエラーなど、発生させ難いテストが容易になる ! mock::double('SampleModel_Suser', [! 'getName' => function() {throw new FuelCoreFuelException("例外発生");}! ]);! ! try {! SampleModel_Suser::getName();! ! } catch (FuelException $e) {! ! $this->assertTrue(true);! throw $e;! }! ! $this->assertTrue(false);! }!
  • 33. AspectMockを使ってみる3 public function test_Privateなメソッドのケース() {! privateメソッドもMock化できる  ※AspectMockの機能ではありませんが、Closureでprivateメソッドが直接呼べる ! // privateメソッドで呼んでるクラスをMock化! mock::double('FuelCoreDate', ['time' => 'hogehoge']);! ! // Privateメソッドを直接呼ぶ! Closure::bind(! function () {! $obj = new SampleModel_Suser();! $ret = $obj->privatefunc();! ! $this->assertEquals('private:hogehoge' . PHP_EOL, $ret);! },! $this,! 'SampleModel_Suser'! )->__invoke();! ! mock::clean();! mock::double('SampleModel_Suser', ['privatefunc' => 'プライベート関数もMock化']);! ! $ret = SampleModel_Suser::callPrivate();! $this->assertEquals('プライベート関数もMock化', $ret);! ! }
  • 34. AspectMockを使ってみる4 <?php! namespace Sample;! ! use FuelCoreDB;! ! class Model_Transaction extends Model {! ! public static function transaction() {! ! if(!DB::in_transaction()) {! DB::start_transaction();! }! ! try {! // DB処理! Model_Orm_User::find();! ! DB::commit_transaction();! ! } catch(FuelException $e) {! if(DB::in_transaction()) {! DB::rollback_transaction();! }! ! throw $e;! }! }! }! 【ケース】  例外が発生したときの挙動をテストしたい    1. トランザクションが無ければ貼る  2. 例外が発生しとき、トランザクションが    貼ってあれば、Rollbackする
  • 35. AspectMockを使ってみる4 public function test_呼び出し回数で挙動を変えるケース() ! {! ! $cnt = 0;! ! $mock = mock::double(! 'FuelCoreDB',! [! 'start_transaction' => true,! 'commit_transaction' => true,! 'rollback_transaction' => true,! 'in_transaction' => function () use (&$cnt) {! if ($cnt == 0) {! $cnt++;! return false;! } elseif ($cnt == 1) {! $cnt++;! return true;! } else {! $cnt++;! return __AM_CONTINUE__; // オリジナルの処理がされる! }! }]);! ! mock::double('SampleModel_Orm_User', [! ! ! ! 'find' => function() {! ! ! ! ! throw new FuelException(“Exception強制発生");}! ! ! ]);! ! try {! SampleModel_Transaction::transaction();! } catch(FuelException $e) {! $mock->verifyInvokedOnce('start_transaction');! $mock->verifyNeverInvoked('commit_transaction');! $mock->verifyInvokedMultipleTimes('in_transaction', 2);! $mock->verifyInvokedOnce('rollback_transaction');! }! } 無名関数の引数に 変数を参照渡しして 呼び出し回数を カウント
  • 36. AspectMockを使ってみる5 【ケース】 ORMでfindした値が、 想定どおりの処理が行われた値で saveされるかを確認したい <?php! ! namespace Sample;! ! class Model_Orm_User extends OrmModel! {! ! <?php! ! namespace Sample;! ! class Model_Update extends Model! {! public static function update()! {! $model = Model_Orm_User::find();! ! foreach($model as $ret) {! $ret['val'] = 'hugehuge';! $ret->save();! }! }! }! protected static $_properties = [! 'id',! 'val',! ];! }!
  • 37. AspectMockを使ってみる5 確認したいORMクラ スを継承してfindし た結果をダミー値で 定義。 ! saveの引数を無名関 数でチェック <?php! Autoloader::add_namespace(‘Sample',! ! ! ! ! APPPATH . 'classes' . DS . 'sample/');! ! class Tests_Model_StubModel extends SampleModel_Orm_User {! ! protected $_data = [‘id','val'];! ! function __construct($id, $val){! $this->_data['id'] = $id;! $this->_data['val'] = $val;! }! } public function test_ORMでの更新値チェックのケース() {! // ORMでDBから値を取得して処理して更新するパターン! $modify_data = 'hugehuge';! ! $data = new Tests_Model_StubModel('1', 'hogehoge');! ! mock::double('SampleModel_Orm_User',! [! 'find' => function ($param) use ($data) {! // ORMのfindは、レコード単位のORMクラス配列が返る! return [$data];! }! ]);! ! mock::double('OrmModel',[! 'save' => function() use ($modify_data) {! FuelCoreTestCase::assertEquals(! ! ! ! $this->_data['val'], $modify_data! ! ! );! }! ]);! ! SampleModel_Update::update();! }!