SlideShare a Scribd company logo
1 of 37
第65回 PHP勉強会
 @東京(Engine Yard)

behat/PHPUnitで始める
     BDD/TDD


2013/02/28(木)   @ビルコム
前置き。
about tchikuba
• 所属
   – ビルコム株式会社
• 自社サービス
   – ReBuy http://rebuy.jp/
   – Okkru https://okkru.jp
• 役割
   – 開発Playing Manager
• いいね!
   – Linux、ビール、宇宙平和
• facebook/twitter
   – tchikuba
ReBuy:facebookコマースアプリ
Okkru:ソーシャルギフト
我々の諸環境
•   OS:CentOS(nifty cloud)
•   Webサーバ:Nginx/Apache
•   DBサーバ:MySQL
•   CIサーバ:Jenkins/SeleniumServer
•   言語:PHP
•   フレームワーク:Zendframework
•   フロント:jQuery(Mobile)
•   SCM:Git
•   BTS:Backlog
聞きたいこと。




•開発プロセス?
•BDD/TDD?
本題。
TDDとは
• Test-Driven Developmentの

• 和訳:テスト駆動開発

• 特徴
  – テストファースト
  – リファクタリング


• メリット
  – スパゲッティの塩分低下
  – 開発アジリティ上昇
BDDとは
• Behavior-Driven Developmentの

• 和訳:振舞駆動開発

• 特徴
  – 自然言語
  – 要求仕様(スペック)


• メリット
  –  け れ 厙
  – オブジェクト指向強化
BDD/TDDのフレームワーク
  • BDD
   – behat + mink
   – PHPSpec
   – Selenium


  • TDD
   – PHPUnit
PHPUnit。
behat。
behat環境設定
• インストール (PHP5.3.4 ≧推奨)
 – Composer
   • vi composer.json
   • curl http://getcomposer.org/installer | php
   • php composer.phar install
 – PHAR
 – Git
• 初期プロジェクト生成
 – behat --init
ディレクトリ構成
• behat_root/
  – features/
     • bootstrap/
        – FeatureContext.php
     • *.feature
  – behat.yml
  – build.xml *for Jenkins

• *.feature:機能+シナリオ(複数可)
• FeatureContext.php:テスト定義
• bootstrap配下はオートロードされる
く却 コマンド
• テスト
 – behat [features/*.feature]


• コンテキスト一覧
 – behat –dl


• スニペット生成
 – behat –f snippets
featureの
Feature: facebook login
  facebook login

  @javascript
  Scenario: facebook login
    Given on facebook login page.
    When input email and password.
    And     press login button
    Then facebook logged in.
Contextの
<?php

use Behat¥Behat¥Context¥ClosuredContextInterface,
  Behat¥Behat¥Context¥TranslatedContextInterface,
  Behat¥Behat¥Context¥BehatContext,
  Behat¥Behat¥Exception¥PendingException;
use Behat¥Gherkin¥Node¥PyStringNode,
  Behat¥Gherkin¥Node¥TableNode;

class FacebookloginContext extends BehatContext
{
   public $mink;

  public function __construct($mink)
  {
     $this->mink = $mink;
  }
(続く)
Contextの
  /**
   * @Given /^on facebook login page¥.$/
   */
  public function onFacebookLoginPage()
  {
     $this->mink->getSession()-
   >visit('http://www.facebook.com/login.php');
  }

  /**
   * @When /^input email and password¥.$/
   */
  public function inputEmailAndPassword()
  {
     $this->mink->getSession()->getPage()->fillField('email',
   MAIN_EMAIL);
     $this->mink->getSession()->getPage()->fillField('pass',
   PASSWORD);
  }
(続く)
Contextの
  /**
   * @Given /^press login button$/
   */
  public function pressLoginButton()
  {
     $this->mink->getSession()->getPage()->pressButton('Log In');
  }

  /**
   * @Then /^facebook logged in¥.$/
   */
  public function facebookLoggedIn()
  {
     assertEquals('https://www.facebook.com/', $this->mink-
   >getSession()->getCurrentUrl());
  }
}
(終わり)
FeatureContextの
<?php

use Behat¥Behat¥Context¥ClosuredContextInterface,
  Behat¥Behat¥Context¥TranslatedContextInterface,
  Behat¥Behat¥Context¥BehatContext,
  Behat¥Behat¥Exception¥PendingException;
use Behat¥Gherkin¥Node¥PyStringNode,
  Behat¥Gherkin¥Node¥TableNode;
use Behat¥MinkExtension¥Context¥MinkContext;

/**
 * Features context.
 */
class FeatureContext extends MinkContext
{

  public $base_url;
  public $mink;
FeatureContextの
     public function __construct(array $parameters)
    {
       // Initialize your context here
       $driver = new ¥Behat¥Mink¥Driver¥Selenium2Driver(
                    SELENIUM2_BROWSER, null, SELENIUM2_URL
       );
       $session = new ¥Behat¥Mink¥Session($driver);
       $this->base_url = OKKRU_BASE_URL;
       $mink = new ¥Behat¥Mink¥Mink();
       $mink->registerSession('okkru', $session);
       $mink->setDefaultSessionName('okkru');
       $this->mink = $mink;

        // use other context
        $this->useContext('fbconnect', new FbconnectContext($mink));
        $this->useContext('facebooklogin', new FacebookloginContext($mink));
        $this->useContext('sendme', new SendmeContext($mink));
        $this->useContext('inputme', new InputmeContext($mink));
        $this->useContext('ticketsend', new TicketsendContext($mink));
        $this->useContext('ticketrecipient', new TicketrecipientContext($mink));
    }

}
り             な           :feature
Feature: sendme
  商品を自分で受け取ることが出来る。

 @javascript
 Scenario: 商品を自分贈りで購入する。
   Given テスト用商品ページにアクセスする。
   When 購入ボタンを押下する。
   Then ログイン画面に遷移する。
   When ログインボタンを押下する。
   Then facebookログインページに遷移する。
   When facebookログインする。
   Then 友人選択画面に遷移する。
   When 友人選択画面で自分贈りを選択する。
   Then 自分の住所情報が入 出来る。
   And     FBから引いた情報が補完されている。
   When 自分の住所情報を入 する。
   And     確認ボタンを押下する。
   Then 確認画面に入       卣が勖単される。
   When 確定ボタンを押下する。
   Then 決済画面に遷移する。
り                    な               :Context
<?php

use Behat¥Behat¥Context¥ClosuredContextInterface,
  Behat¥Behat¥Context¥TranslatedContextInterface,
  Behat¥Behat¥Context¥BehatContext,
  Behat¥Behat¥Exception¥PendingException;
use Behat¥Gherkin¥Node¥PyStringNode,
  Behat¥Gherkin¥Node¥TableNode;

class SendmeContext extends BehatContext
{

  public $base_url;
  public $mink;

  public function __construct($mink)
  {
     $this->base_url = OKKRU_BASE_URL;
     $this->mink = $mink;
  }
(続く)
り                    な                :Context
/**
   * @Given /^テスト用商品ページにアクセスする。$/
   */
  public function accessDetail()
  {
      $this->mink->getSession()->visit($this->base_url .
   '/fbec_front/gift/detail/shop_id/' . SHOP_ID . '/product_id/' .
   PRODUCT_ID);
  }

  /**
   * @When /^購入ボタンを押下する。$/
   */
  public function pressProductButton()
  {
     $this->mink->getSession()->getPage()->clickLink('product-
   button');
  }
り                   な              :Context
/**
   * @Then /^ログイン画面に遷移する。$/
   */
  public function assertDetail2FBlogin()
  {
     $this->mink->assertSession()-
   >addressMatches("/fbec_front¥/gift¥/login¥/url/");
  }

  /**
   * @When /^ログインボタンを押下する。$/
   */
  public function pressConnectButton()
  {
     $this->mink->getSession()->wait(3000, "$('#login_btn').length
   > 0");
     $this->mink->getSession()->getPage()->clickLink('login_btn');
  }
り                         な                   :Context
/**
   * @Then /^facebookログインページに遷移する。$/
   */
  public function assertFBConnect2FBLogin()
  {
     $expected_url = 'https://www.facebook.com';
     $this->mink->getSession()->wait(3000);
     $actual_url = $this->mink->getSession()->getCurrentUrl();
     $this->getMainContext()->getSubContext('fbconnect')->assertContainUrl($expected_url,
    $actual_url);
  }

 /**
  * @When /^facebookログインする。$/
  */
 public function facebookLogin()
 {
    $this->getMainContext()->getSubContext('facebooklogin')->inputEmailAndPassword();
    $this->getMainContext()->getSubContext('facebooklogin')->pressLoginButton();
 }
り                  な               :Context
/**
   * @Then /^友人選択画面に遷移する。$/
   */
  public function displayFriendsSelect()
  {
     $this->mink->assertSession()-
   >addressMatches("/fbec_front¥/gift¥/friends¥/shop_id¥/" .
   SHOP_ID . "/");
  }

  /**
   * @When /^友人選択画面で自分贈りを選択する。$/
   */
  public function selectBuymyself()
  {
     $this->mink->getSession()->getPage()->clickLink('buymyself-
   button');
  }
り                       な                   :Context
/**
   * @Then /^自分の住所情報が入 出来る。$/
   */
  public function assertInputMyAddress()
  {
     $this->mink->assertSession()->pageTextContains('※贈り主情報:フリガナ');
     $this->mink->assertSession()->pageTextContains('※贈り主情報:メールアドレス');
     $this->mink->assertSession()->pageTextContains('※お届け先:   番号');
     $this->mink->assertSession()->pageTextContains('※お届け先:都道府県');
     $this->mink->assertSession()->pageTextContains('※お届け先:住所');
     $this->mink->assertSession()->pageTextContains('※お届け先:電話番号');
  }

 /**
  * @Given /^FBから引いた情報が補完されている。$/
  */
 public function assertSetFBinfo()
 {
    assertEquals(LAST_NAME_FB, $this->mink->getSession()->getPage()-
   >findById('last_name')->getValue());
    assertEquals(FIRST_NAME_FB, $this->mink->getSession()->getPage()-
   >findById('first_name')->getValue());
    assertEquals(MAIN_EMAIL, $this->mink->getSession()->getPage()->findById('email')-
   >getValue());
 }
り                              な                       :Context
/**
   * @When /^自分の住所情報を入 する。$/
   */
  public function inputMyAddress()
  {
     $this->mink->getSession()->getPage()->fillField('last_name', LAST_NAME_INPUT);
     $this->mink->getSession()->getPage()->fillField('first_name', FIRST_NAME_INPUT);
     $this->mink->getSession()->getPage()->fillField('last_name_kana', LAST_NAME_KANA);
     $this->mink->getSession()->getPage()->fillField('first_name_kana', FIRST_NAME_KANA);
     $this->mink->getSession()->getPage()->fillField('email', INPUT_EMAIL);
     $this->mink->getSession()->getPage()->fillField('zip3', ZIP3);
     $this->mink->getSession()->getPage()->fillField('zip4', ZIP4);
     $this->mink->getSession()->getPage()->selectFieldOption('prefecture', PREFECTURE);
     $this->mink->getSession()->getPage()->fillField('address', ADDRESS);
     $this->mink->getSession()->getPage()->fillField('address2', ADDRESS2);
     $this->mink->getSession()->getPage()->fillField('phone1', PHONE1);
     $this->mink->getSession()->getPage()->fillField('phone2', PHONE2);
     $this->mink->getSession()->getPage()->fillField('phone3', PHONE3);
     $this->mink->getSession()->getPage()->fillField('note', NOTE_SENDME);
  }

 /**
  * @Given /^確認ボタンを押下する。$/
  */
 public function pressSubmitShipmentButton()
 {
    $this->mink->getSession()->getPage()->clickLink('submit-shipment');
 }
り                       な                 :Context
/**
   * @Then /^確認画面に入             卣が勖単される。$/
   */
  public function assertDisplayInput()
  {
     $this->mink->assertSession()->pageTextContains(PRODUCT_NAME);
     $this->mink->assertSession()->pageTextContains(SHOP_NAME);
     $this->mink->assertSession()->pageTextContains(PRODUCT_PRICE);
     $this->mink->assertSession()->pageTextContains(PRODUCT_AND_WRAPPING_PRICE);
     $this->mink->assertSession()->pageTextContains(PRODUCT_ALL_PRICE);
     $this->mink->assertSession()->pageTextContains(LAST_NAME_INPUT . ' ' .
    FIRST_NAME_INPUT);
     $this->mink->assertSession()->pageTextContains(LAST_NAME_KANA . ' ' .
    FIRST_NAME_KANA);
     $this->mink->assertSession()->pageTextContains(INPUT_EMAIL);
     $this->mink->assertSession()->pageTextContains(ZIP3 . '-' . ZIP4);
     $this->mink->assertSession()->pageTextContains(PREFECTURE);
     $this->mink->assertSession()->pageTextContains(ADDRESS);
     $this->mink->assertSession()->pageTextContains(ADDRESS2);
     $this->mink->assertSession()->pageTextContains(PHONE1 . '-' . PHONE2 . '-' .
    PHONE3);
     $this->mink->assertSession()->pageTextContains(NOTE_SENDME);
  }
り                      な                   :Context
/**
   * @When /^確定ボタンを押下する。$/
   */
  public function pressSubmitConfirm()
  {
     $this->mink->getSession()->getPage()->clickLink('submit-confirm');
  }

    /**
     * @Then /^決済画面に遷移する。$/
     */
    public function assertConfirm2SBPS()
    {
       $this->mink->getSession()->wait(5000, 'typeof window.jQuery == "function"');
       $this->mink->getSession()->wait(3000, 'window.document.getElementById("#title") >
      0');
       $this->mink->assertSession()->addressMatches("/¥/f01¥/FepBuyInfoReceive.do/");
    }

}
でも。
まとめ
• メリット
  – BDDで「なんちゃって」アジャイルを回避
  – behatでテストファーストすることで事前の洗い出しが
         に


• デメリット
  – スケジュールに追われる場合後回しになりがちで開発
    プロセスを徹底するのが難しい
  – CIサーバに る定期  でアンドン化して 続メンテ
    ナンスしないと無効なテストケースが増殖
\(^o^)/オワタ
  facebook/twitterアカウント:tchikuba

More Related Content

What's hot

Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考える
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考えるGoのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考える
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考える
pospome
 
Twitterのsnowflakeについて
TwitterのsnowflakeについてTwitterのsnowflakeについて
Twitterのsnowflakeについて
moai kids
 
オブジェクト指向エクササイズのススメ
オブジェクト指向エクササイズのススメオブジェクト指向エクササイズのススメ
オブジェクト指向エクササイズのススメ
Yoji Kanno
 

What's hot (20)

PHPの今とこれから2022
PHPの今とこれから2022PHPの今とこれから2022
PHPの今とこれから2022
 
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考える
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考えるGoのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考える
Goのサーバサイド実装におけるレイヤ設計とレイヤ内実装について考える
 
SQLアンチパターン~ファントムファイル
SQLアンチパターン~ファントムファイルSQLアンチパターン~ファントムファイル
SQLアンチパターン~ファントムファイル
 
Hack言語に賭けたチームの話
Hack言語に賭けたチームの話Hack言語に賭けたチームの話
Hack言語に賭けたチームの話
 
Spring と TDD
Spring と TDDSpring と TDD
Spring と TDD
 
いまさら聞けないパスワードの取り扱い方
いまさら聞けないパスワードの取り扱い方いまさら聞けないパスワードの取り扱い方
いまさら聞けないパスワードの取り扱い方
 
Twitterのsnowflakeについて
TwitterのsnowflakeについてTwitterのsnowflakeについて
Twitterのsnowflakeについて
 
Redmineを快適に使うためのおすすめ初期設定
Redmineを快適に使うためのおすすめ初期設定Redmineを快適に使うためのおすすめ初期設定
Redmineを快適に使うためのおすすめ初期設定
 
Laravelでfacadeを使わない開発
Laravelでfacadeを使わない開発Laravelでfacadeを使わない開発
Laravelでfacadeを使わない開発
 
unique_ptrにポインタ以外のものを持たせるとき
unique_ptrにポインタ以外のものを持たせるときunique_ptrにポインタ以外のものを持たせるとき
unique_ptrにポインタ以外のものを持たせるとき
 
PWA Beginners 勉強会 #4 A2HSのちょっと深い話v2
PWA Beginners 勉強会 #4 A2HSのちょっと深い話v2PWA Beginners 勉強会 #4 A2HSのちょっと深い話v2
PWA Beginners 勉強会 #4 A2HSのちょっと深い話v2
 
イミュータブルデータモデルの極意
イミュータブルデータモデルの極意イミュータブルデータモデルの極意
イミュータブルデータモデルの極意
 
SIROK技術勉強会 #1 「Reactってなんだ?」
SIROK技術勉強会 #1 「Reactってなんだ?」SIROK技術勉強会 #1 「Reactってなんだ?」
SIROK技術勉強会 #1 「Reactってなんだ?」
 
SSH力をつけよう
SSH力をつけようSSH力をつけよう
SSH力をつけよう
 
オブジェクト指向エクササイズのススメ
オブジェクト指向エクササイズのススメオブジェクト指向エクササイズのススメ
オブジェクト指向エクササイズのススメ
 
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
 
PHP-FPM の子プロセス制御方法と設定をおさらいしよう
PHP-FPM の子プロセス制御方法と設定をおさらいしようPHP-FPM の子プロセス制御方法と設定をおさらいしよう
PHP-FPM の子プロセス制御方法と設定をおさらいしよう
 
react-scriptsはwebpackで何をしているのか
react-scriptsはwebpackで何をしているのかreact-scriptsはwebpackで何をしているのか
react-scriptsはwebpackで何をしているのか
 
Getting Started with Spring Authorization Server
Getting Started with Spring Authorization ServerGetting Started with Spring Authorization Server
Getting Started with Spring Authorization Server
 
ドメイン駆動設計入門
ドメイン駆動設計入門ドメイン駆動設計入門
ドメイン駆動設計入門
 

Viewers also liked

Viewers also liked (20)

Behatで行う、E2Eテスト入門
Behatで行う、E2Eテスト入門Behatで行う、E2Eテスト入門
Behatで行う、E2Eテスト入門
 
Behat+Symfony2ではじめるBDD超入門
Behat+Symfony2ではじめるBDD超入門Behat+Symfony2ではじめるBDD超入門
Behat+Symfony2ではじめるBDD超入門
 
「スピード」と「品質」を実現するPHP開発チームの取り組み~AngularJS+FuelPHP+AspectMock~
「スピード」と「品質」を実現するPHP開発チームの取り組み~AngularJS+FuelPHP+AspectMock~「スピード」と「品質」を実現するPHP開発チームの取り組み~AngularJS+FuelPHP+AspectMock~
「スピード」と「品質」を実現するPHP開発チームの取り組み~AngularJS+FuelPHP+AspectMock~
 
phpspecで始めるBDD
phpspecで始めるBDDphpspecで始めるBDD
phpspecで始めるBDD
 
Codeceptionの受け入れテストをマネる
Codeceptionの受け入れテストをマネるCodeceptionの受け入れテストをマネる
Codeceptionの受け入れテストをマネる
 
TDDBC Fukuoka Day1
TDDBC Fukuoka Day1TDDBC Fukuoka Day1
TDDBC Fukuoka Day1
 
BPStudy20121221
BPStudy20121221BPStudy20121221
BPStudy20121221
 
テスト自動化ツール[Selenium]を検討してみて
テスト自動化ツール[Selenium]を検討してみてテスト自動化ツール[Selenium]を検討してみて
テスト自動化ツール[Selenium]を検討してみて
 
bashでWebブラウザ(Selenium WebDriver)を動かした話
bashでWebブラウザ(Selenium WebDriver)を動かした話bashでWebブラウザ(Selenium WebDriver)を動かした話
bashでWebブラウザ(Selenium WebDriver)を動かした話
 
Scrutinizer CIでPHPも静的解析 #phpstudy
Scrutinizer CIでPHPも静的解析 #phpstudyScrutinizer CIでPHPも静的解析 #phpstudy
Scrutinizer CIでPHPも静的解析 #phpstudy
 
最強のPHP統合開発環境 PHPStorm
最強のPHP統合開発環境 PHPStorm最強のPHP統合開発環境 PHPStorm
最強のPHP統合開発環境 PHPStorm
 
はじめてのUser-Agent文字列
はじめてのUser-Agent文字列はじめてのUser-Agent文字列
はじめてのUser-Agent文字列
 
Codeceptionことはじめ
CodeceptionことはじめCodeceptionことはじめ
Codeceptionことはじめ
 
「Selenium実践入門」で学ぶテスト自動化の世界
「Selenium実践入門」で学ぶテスト自動化の世界「Selenium実践入門」で学ぶテスト自動化の世界
「Selenium実践入門」で学ぶテスト自動化の世界
 
5分でわかる静的解析入門
5分でわかる静的解析入門5分でわかる静的解析入門
5分でわかる静的解析入門
 
たのしい独自フレームワーク
たのしい独自フレームワークたのしい独自フレームワーク
たのしい独自フレームワーク
 
いつでも聞けるTDD入門 #TDDBC_NAGOYA
いつでも聞けるTDD入門 #TDDBC_NAGOYAいつでも聞けるTDD入門 #TDDBC_NAGOYA
いつでも聞けるTDD入門 #TDDBC_NAGOYA
 
今、最もイケてるPHPフレームワークLaravel4
今、最もイケてるPHPフレームワークLaravel4今、最もイケてるPHPフレームワークLaravel4
今、最もイケてるPHPフレームワークLaravel4
 
脆弱性もバグ、だからテストしよう PHPカンファンレス2015
脆弱性もバグ、だからテストしよう PHPカンファンレス2015脆弱性もバグ、だからテストしよう PHPカンファンレス2015
脆弱性もバグ、だからテストしよう PHPカンファンレス2015
 
AWS October Webinar Series - Introducing Amazon Elasticsearch Service
AWS October Webinar Series - Introducing Amazon Elasticsearch ServiceAWS October Webinar Series - Introducing Amazon Elasticsearch Service
AWS October Webinar Series - Introducing Amazon Elasticsearch Service
 

Similar to behatで始めるBDD

WordPressで提供するWeb API
WordPressで提供するWeb APIWordPressで提供するWeb API
WordPressで提供するWeb API
Yuko Toriyama
 
Mojoliciousをウェブ制作現場で使ってみてる
Mojoliciousをウェブ制作現場で使ってみてるMojoliciousをウェブ制作現場で使ってみてる
Mojoliciousをウェブ制作現場で使ってみてる
jamadam
 
3ヶ月でリア充大学生がFBアプリをリリースするまで @macrocro
3ヶ月でリア充大学生がFBアプリをリリースするまで @macrocro3ヶ月でリア充大学生がFBアプリをリリースするまで @macrocro
3ヶ月でリア充大学生がFBアプリをリリースするまで @macrocro
Yuuki Kuroda
 
WordBech Osaka No.28
WordBech Osaka No.28WordBech Osaka No.28
WordBech Osaka No.28
Kite Koga
 
モバイル開発@symfony
モバイル開発@symfonyモバイル開発@symfony
モバイル開発@symfony
Daichi Kamemoto
 
SocialWeb Conference vol.5 OpenSocial Night #2
SocialWeb Conference vol.5 OpenSocial Night #2SocialWeb Conference vol.5 OpenSocial Night #2
SocialWeb Conference vol.5 OpenSocial Night #2
Nobuhiro Nakajima
 

Similar to behatで始めるBDD (20)

omoon.org の裏側 〜FuelPHP の task 活用例〜
omoon.org の裏側 〜FuelPHP の task 活用例〜omoon.org の裏側 〜FuelPHP の task 活用例〜
omoon.org の裏側 〜FuelPHP の task 活用例〜
 
Try Jetpack
Try JetpackTry Jetpack
Try Jetpack
 
WordPressで提供するWeb API
WordPressで提供するWeb APIWordPressで提供するWeb API
WordPressで提供するWeb API
 
Mojoliciousをウェブ制作現場で使ってみてる
Mojoliciousをウェブ制作現場で使ってみてるMojoliciousをウェブ制作現場で使ってみてる
Mojoliciousをウェブ制作現場で使ってみてる
 
Inside Movable Type
Inside Movable TypeInside Movable Type
Inside Movable Type
 
3ヶ月でリア充大学生がFBアプリをリリースするまで @macrocro
3ヶ月でリア充大学生がFBアプリをリリースするまで @macrocro3ヶ月でリア充大学生がFBアプリをリリースするまで @macrocro
3ヶ月でリア充大学生がFBアプリをリリースするまで @macrocro
 
Ci tutorial
Ci tutorialCi tutorial
Ci tutorial
 
WordBech Osaka No.28
WordBech Osaka No.28WordBech Osaka No.28
WordBech Osaka No.28
 
リアルFacebookガジェットを作った(ロングバージョン)
リアルFacebookガジェットを作った(ロングバージョン)リアルFacebookガジェットを作った(ロングバージョン)
リアルFacebookガジェットを作った(ロングバージョン)
 
「Windows 8 ストア アプリ開発 tips」 hokuriku.net vol.11 (2013年1月26日)
「Windows 8 ストア アプリ開発 tips」  hokuriku.net vol.11 (2013年1月26日)「Windows 8 ストア アプリ開発 tips」  hokuriku.net vol.11 (2013年1月26日)
「Windows 8 ストア アプリ開発 tips」 hokuriku.net vol.11 (2013年1月26日)
 
WordBench京都 9月号:kintone×WordPressハンズオン
WordBench京都 9月号:kintone×WordPressハンズオンWordBench京都 9月号:kintone×WordPressハンズオン
WordBench京都 9月号:kintone×WordPressハンズオン
 
FuelPHPをさわってみて
FuelPHPをさわってみてFuelPHPをさわってみて
FuelPHPをさわってみて
 
SpringOne 2GX 2014 参加報告 & Spring 4.1について #jsug
SpringOne 2GX 2014 参加報告 & Spring 4.1について #jsugSpringOne 2GX 2014 参加報告 & Spring 4.1について #jsug
SpringOne 2GX 2014 参加報告 & Spring 4.1について #jsug
 
モバイル開発@symfony
モバイル開発@symfonyモバイル開発@symfony
モバイル開発@symfony
 
[東京] JapanSharePointGroup 勉強会 #2
[東京] JapanSharePointGroup 勉強会 #2[東京] JapanSharePointGroup 勉強会 #2
[東京] JapanSharePointGroup 勉強会 #2
 
WordPress widget api
WordPress widget apiWordPress widget api
WordPress widget api
 
test
testtest
test
 
SocialWeb Conference vol.5 OpenSocial Night #2
SocialWeb Conference vol.5 OpenSocial Night #2SocialWeb Conference vol.5 OpenSocial Night #2
SocialWeb Conference vol.5 OpenSocial Night #2
 
初めての Data api
初めての Data api初めての Data api
初めての Data api
 
Data apiで実現 進化するwebの世界
Data apiで実現 進化するwebの世界Data apiで実現 進化するwebの世界
Data apiで実現 進化するwebの世界
 

More from Tsutomu Chikuba

Php unit extensions_selenium2_testcaseによる結合試験でらくらくテスト♪
Php unit extensions_selenium2_testcaseによる結合試験でらくらくテスト♪Php unit extensions_selenium2_testcaseによる結合試験でらくらくテスト♪
Php unit extensions_selenium2_testcaseによる結合試験でらくらくテスト♪
Tsutomu Chikuba
 
【参観レポート】Lean startupnight real startup dialog
【参観レポート】Lean startupnight   real startup dialog【参観レポート】Lean startupnight   real startup dialog
【参観レポート】Lean startupnight real startup dialog
Tsutomu Chikuba
 
リーンソフトウェア開発で始めるFacebookアプリ開発
リーンソフトウェア開発で始めるFacebookアプリ開発リーンソフトウェア開発で始めるFacebookアプリ開発
リーンソフトウェア開発で始めるFacebookアプリ開発
Tsutomu Chikuba
 

More from Tsutomu Chikuba (11)

Megurorb 15 lt
Megurorb 15 ltMegurorb 15 lt
Megurorb 15 lt
 
Ebisu.rb#15LT「turnipいいよturnip」
Ebisu.rb#15LT「turnipいいよturnip」Ebisu.rb#15LT「turnipいいよturnip」
Ebisu.rb#15LT「turnipいいよturnip」
 
Meguro.rb#3LT「Ruby入門書を書いてて得られた知見5選+3α」
Meguro.rb#3LT「Ruby入門書を書いてて得られた知見5選+3α」Meguro.rb#3LT「Ruby入門書を書いてて得られた知見5選+3α」
Meguro.rb#3LT「Ruby入門書を書いてて得られた知見5選+3α」
 
オープンデータ利活用への道のり@未来型不動産サービス「IESHIL(イエシル)」の事例
オープンデータ利活用への道のり@未来型不動産サービス「IESHIL(イエシル)」の事例オープンデータ利活用への道のり@未来型不動産サービス「IESHIL(イエシル)」の事例
オープンデータ利活用への道のり@未来型不動産サービス「IESHIL(イエシル)」の事例
 
turnipとcapybaraとBDDと私
turnipとcapybaraとBDDと私turnipとcapybaraとBDDと私
turnipとcapybaraとBDDと私
 
いよいよPHPerもリーンで行こう
いよいよPHPerもリーンで行こういよいよPHPerもリーンで行こう
いよいよPHPerもリーンで行こう
 
Php unit extensions_selenium2_testcaseによる結合試験でらくらくテスト♪
Php unit extensions_selenium2_testcaseによる結合試験でらくらくテスト♪Php unit extensions_selenium2_testcaseによる結合試験でらくらくテスト♪
Php unit extensions_selenium2_testcaseによる結合試験でらくらくテスト♪
 
ビジネス的に高価値なアジャイルテスト
ビジネス的に高価値なアジャイルテストビジネス的に高価値なアジャイルテスト
ビジネス的に高価値なアジャイルテスト
 
【参観レポート】Lean startupnight real startup dialog
【参観レポート】Lean startupnight   real startup dialog【参観レポート】Lean startupnight   real startup dialog
【参観レポート】Lean startupnight real startup dialog
 
PHP agile test tips
PHP agile test tipsPHP agile test tips
PHP agile test tips
 
リーンソフトウェア開発で始めるFacebookアプリ開発
リーンソフトウェア開発で始めるFacebookアプリ開発リーンソフトウェア開発で始めるFacebookアプリ開発
リーンソフトウェア開発で始めるFacebookアプリ開発
 

Recently uploaded

研究紹介スライド: オフライン強化学習に基づくロボティックスワームの制御器の設計
研究紹介スライド: オフライン強化学習に基づくロボティックスワームの制御器の設計研究紹介スライド: オフライン強化学習に基づくロボティックスワームの制御器の設計
研究紹介スライド: オフライン強化学習に基づくロボティックスワームの制御器の設計
atsushi061452
 

Recently uploaded (16)

情報を表現するときのポイント
情報を表現するときのポイント情報を表現するときのポイント
情報を表現するときのポイント
 
クラウド時代におけるSREとUPWARDの取組ーUPWARD株式会社 CTO門畑
クラウド時代におけるSREとUPWARDの取組ーUPWARD株式会社 CTO門畑クラウド時代におけるSREとUPWARDの取組ーUPWARD株式会社 CTO門畑
クラウド時代におけるSREとUPWARDの取組ーUPWARD株式会社 CTO門畑
 
LoRaWAN無位置ロープ型水漏れセンサー WL03A-LB/LSカタログ ファイル
LoRaWAN無位置ロープ型水漏れセンサー WL03A-LB/LSカタログ ファイルLoRaWAN無位置ロープ型水漏れセンサー WL03A-LB/LSカタログ ファイル
LoRaWAN無位置ロープ型水漏れセンサー WL03A-LB/LSカタログ ファイル
 
ロボットマニピュレーションの作業・動作計画 / rosjp_planning_for_robotic_manipulation_20240521
ロボットマニピュレーションの作業・動作計画 / rosjp_planning_for_robotic_manipulation_20240521ロボットマニピュレーションの作業・動作計画 / rosjp_planning_for_robotic_manipulation_20240521
ロボットマニピュレーションの作業・動作計画 / rosjp_planning_for_robotic_manipulation_20240521
 
Hyperledger Fabricコミュニティ活動体験& Hyperledger Fabric最新状況ご紹介
Hyperledger Fabricコミュニティ活動体験& Hyperledger Fabric最新状況ご紹介Hyperledger Fabricコミュニティ活動体験& Hyperledger Fabric最新状況ご紹介
Hyperledger Fabricコミュニティ活動体験& Hyperledger Fabric最新状況ご紹介
 
20240523_IoTLT_vol111_kitazaki_v1___.pdf
20240523_IoTLT_vol111_kitazaki_v1___.pdf20240523_IoTLT_vol111_kitazaki_v1___.pdf
20240523_IoTLT_vol111_kitazaki_v1___.pdf
 
論文紹介:ViTPose: Simple Vision Transformer Baselines for Human Pose Estimation
論文紹介:ViTPose: Simple Vision Transformer Baselines for Human Pose Estimation論文紹介:ViTPose: Simple Vision Transformer Baselines for Human Pose Estimation
論文紹介:ViTPose: Simple Vision Transformer Baselines for Human Pose Estimation
 
2024年5月17日 先駆的科学計算フォーラム2024 機械学習を用いた新たなゲーム体験の創出の応用
2024年5月17日 先駆的科学計算フォーラム2024 機械学習を用いた新たなゲーム体験の創出の応用2024年5月17日 先駆的科学計算フォーラム2024 機械学習を用いた新たなゲーム体験の創出の応用
2024年5月17日 先駆的科学計算フォーラム2024 機械学習を用いた新たなゲーム体験の創出の応用
 
Keywordmap overview material/CINC.co.ltd
Keywordmap overview material/CINC.co.ltdKeywordmap overview material/CINC.co.ltd
Keywordmap overview material/CINC.co.ltd
 
5/22 第23回 Customer系エンジニア座談会のスライド 公開用 西口瑛一
5/22 第23回 Customer系エンジニア座談会のスライド 公開用 西口瑛一5/22 第23回 Customer系エンジニア座談会のスライド 公開用 西口瑛一
5/22 第23回 Customer系エンジニア座談会のスライド 公開用 西口瑛一
 
MPAなWebフレームワーク、Astroの紹介 (その1) 2024/05/17の勉強会で発表されたものです。
MPAなWebフレームワーク、Astroの紹介 (その1) 2024/05/17の勉強会で発表されたものです。MPAなWebフレームワーク、Astroの紹介 (その1) 2024/05/17の勉強会で発表されたものです。
MPAなWebフレームワーク、Astroの紹介 (その1) 2024/05/17の勉強会で発表されたものです。
 
部内勉強会(IT用語ざっくり学習) 実施日:2024年5月17日(金) 対象者:営業部社員
部内勉強会(IT用語ざっくり学習) 実施日:2024年5月17日(金) 対象者:営業部社員部内勉強会(IT用語ざっくり学習) 実施日:2024年5月17日(金) 対象者:営業部社員
部内勉強会(IT用語ざっくり学習) 実施日:2024年5月17日(金) 対象者:営業部社員
 
Amazon Cognitoで実装するパスキー (Security-JAWS【第33回】 勉強会)
Amazon Cognitoで実装するパスキー (Security-JAWS【第33回】 勉強会)Amazon Cognitoで実装するパスキー (Security-JAWS【第33回】 勉強会)
Amazon Cognitoで実装するパスキー (Security-JAWS【第33回】 勉強会)
 
ネットワーク可視化 振る舞い検知(NDR)ご紹介_キンドリル202405.pdf
ネットワーク可視化 振る舞い検知(NDR)ご紹介_キンドリル202405.pdfネットワーク可視化 振る舞い検知(NDR)ご紹介_キンドリル202405.pdf
ネットワーク可視化 振る舞い検知(NDR)ご紹介_キンドリル202405.pdf
 
研究紹介スライド: オフライン強化学習に基づくロボティックスワームの制御器の設計
研究紹介スライド: オフライン強化学習に基づくロボティックスワームの制御器の設計研究紹介スライド: オフライン強化学習に基づくロボティックスワームの制御器の設計
研究紹介スライド: オフライン強化学習に基づくロボティックスワームの制御器の設計
 
Intranet Development v1.0 (TSG LIVE! 12 LT )
Intranet Development v1.0 (TSG LIVE! 12 LT )Intranet Development v1.0 (TSG LIVE! 12 LT )
Intranet Development v1.0 (TSG LIVE! 12 LT )
 

behatで始めるBDD

  • 1. 第65回 PHP勉強会 @東京(Engine Yard) behat/PHPUnitで始める BDD/TDD 2013/02/28(木) @ビルコム
  • 3. about tchikuba • 所属 – ビルコム株式会社 • 自社サービス – ReBuy http://rebuy.jp/ – Okkru https://okkru.jp • 役割 – 開発Playing Manager • いいね! – Linux、ビール、宇宙平和 • facebook/twitter – tchikuba
  • 6. 我々の諸環境 • OS:CentOS(nifty cloud) • Webサーバ:Nginx/Apache • DBサーバ:MySQL • CIサーバ:Jenkins/SeleniumServer • 言語:PHP • フレームワーク:Zendframework • フロント:jQuery(Mobile) • SCM:Git • BTS:Backlog
  • 9. TDDとは • Test-Driven Developmentの • 和訳:テスト駆動開発 • 特徴 – テストファースト – リファクタリング • メリット – スパゲッティの塩分低下 – 開発アジリティ上昇
  • 10. BDDとは • Behavior-Driven Developmentの • 和訳:振舞駆動開発 • 特徴 – 自然言語 – 要求仕様(スペック) • メリット – け れ 厙 – オブジェクト指向強化
  • 11. BDD/TDDのフレームワーク • BDD – behat + mink – PHPSpec – Selenium • TDD – PHPUnit
  • 13.
  • 15. behat環境設定 • インストール (PHP5.3.4 ≧推奨) – Composer • vi composer.json • curl http://getcomposer.org/installer | php • php composer.phar install – PHAR – Git • 初期プロジェクト生成 – behat --init
  • 16. ディレクトリ構成 • behat_root/ – features/ • bootstrap/ – FeatureContext.php • *.feature – behat.yml – build.xml *for Jenkins • *.feature:機能+シナリオ(複数可) • FeatureContext.php:テスト定義 • bootstrap配下はオートロードされる
  • 17. く却 コマンド • テスト – behat [features/*.feature] • コンテキスト一覧 – behat –dl • スニペット生成 – behat –f snippets
  • 18. featureの Feature: facebook login facebook login @javascript Scenario: facebook login Given on facebook login page. When input email and password. And press login button Then facebook logged in.
  • 19. Contextの <?php use Behat¥Behat¥Context¥ClosuredContextInterface, Behat¥Behat¥Context¥TranslatedContextInterface, Behat¥Behat¥Context¥BehatContext, Behat¥Behat¥Exception¥PendingException; use Behat¥Gherkin¥Node¥PyStringNode, Behat¥Gherkin¥Node¥TableNode; class FacebookloginContext extends BehatContext { public $mink; public function __construct($mink) { $this->mink = $mink; } (続く)
  • 20. Contextの /** * @Given /^on facebook login page¥.$/ */ public function onFacebookLoginPage() { $this->mink->getSession()- >visit('http://www.facebook.com/login.php'); } /** * @When /^input email and password¥.$/ */ public function inputEmailAndPassword() { $this->mink->getSession()->getPage()->fillField('email', MAIN_EMAIL); $this->mink->getSession()->getPage()->fillField('pass', PASSWORD); } (続く)
  • 21. Contextの /** * @Given /^press login button$/ */ public function pressLoginButton() { $this->mink->getSession()->getPage()->pressButton('Log In'); } /** * @Then /^facebook logged in¥.$/ */ public function facebookLoggedIn() { assertEquals('https://www.facebook.com/', $this->mink- >getSession()->getCurrentUrl()); } } (終わり)
  • 22. FeatureContextの <?php use Behat¥Behat¥Context¥ClosuredContextInterface, Behat¥Behat¥Context¥TranslatedContextInterface, Behat¥Behat¥Context¥BehatContext, Behat¥Behat¥Exception¥PendingException; use Behat¥Gherkin¥Node¥PyStringNode, Behat¥Gherkin¥Node¥TableNode; use Behat¥MinkExtension¥Context¥MinkContext; /** * Features context. */ class FeatureContext extends MinkContext { public $base_url; public $mink;
  • 23. FeatureContextの public function __construct(array $parameters) { // Initialize your context here $driver = new ¥Behat¥Mink¥Driver¥Selenium2Driver( SELENIUM2_BROWSER, null, SELENIUM2_URL ); $session = new ¥Behat¥Mink¥Session($driver); $this->base_url = OKKRU_BASE_URL; $mink = new ¥Behat¥Mink¥Mink(); $mink->registerSession('okkru', $session); $mink->setDefaultSessionName('okkru'); $this->mink = $mink; // use other context $this->useContext('fbconnect', new FbconnectContext($mink)); $this->useContext('facebooklogin', new FacebookloginContext($mink)); $this->useContext('sendme', new SendmeContext($mink)); $this->useContext('inputme', new InputmeContext($mink)); $this->useContext('ticketsend', new TicketsendContext($mink)); $this->useContext('ticketrecipient', new TicketrecipientContext($mink)); } }
  • 24. な :feature Feature: sendme 商品を自分で受け取ることが出来る。 @javascript Scenario: 商品を自分贈りで購入する。 Given テスト用商品ページにアクセスする。 When 購入ボタンを押下する。 Then ログイン画面に遷移する。 When ログインボタンを押下する。 Then facebookログインページに遷移する。 When facebookログインする。 Then 友人選択画面に遷移する。 When 友人選択画面で自分贈りを選択する。 Then 自分の住所情報が入 出来る。 And FBから引いた情報が補完されている。 When 自分の住所情報を入 する。 And 確認ボタンを押下する。 Then 確認画面に入 卣が勖単される。 When 確定ボタンを押下する。 Then 決済画面に遷移する。
  • 25. な :Context <?php use Behat¥Behat¥Context¥ClosuredContextInterface, Behat¥Behat¥Context¥TranslatedContextInterface, Behat¥Behat¥Context¥BehatContext, Behat¥Behat¥Exception¥PendingException; use Behat¥Gherkin¥Node¥PyStringNode, Behat¥Gherkin¥Node¥TableNode; class SendmeContext extends BehatContext { public $base_url; public $mink; public function __construct($mink) { $this->base_url = OKKRU_BASE_URL; $this->mink = $mink; } (続く)
  • 26. な :Context /** * @Given /^テスト用商品ページにアクセスする。$/ */ public function accessDetail() { $this->mink->getSession()->visit($this->base_url . '/fbec_front/gift/detail/shop_id/' . SHOP_ID . '/product_id/' . PRODUCT_ID); } /** * @When /^購入ボタンを押下する。$/ */ public function pressProductButton() { $this->mink->getSession()->getPage()->clickLink('product- button'); }
  • 27. な :Context /** * @Then /^ログイン画面に遷移する。$/ */ public function assertDetail2FBlogin() { $this->mink->assertSession()- >addressMatches("/fbec_front¥/gift¥/login¥/url/"); } /** * @When /^ログインボタンを押下する。$/ */ public function pressConnectButton() { $this->mink->getSession()->wait(3000, "$('#login_btn').length > 0"); $this->mink->getSession()->getPage()->clickLink('login_btn'); }
  • 28. な :Context /** * @Then /^facebookログインページに遷移する。$/ */ public function assertFBConnect2FBLogin() { $expected_url = 'https://www.facebook.com'; $this->mink->getSession()->wait(3000); $actual_url = $this->mink->getSession()->getCurrentUrl(); $this->getMainContext()->getSubContext('fbconnect')->assertContainUrl($expected_url, $actual_url); } /** * @When /^facebookログインする。$/ */ public function facebookLogin() { $this->getMainContext()->getSubContext('facebooklogin')->inputEmailAndPassword(); $this->getMainContext()->getSubContext('facebooklogin')->pressLoginButton(); }
  • 29. な :Context /** * @Then /^友人選択画面に遷移する。$/ */ public function displayFriendsSelect() { $this->mink->assertSession()- >addressMatches("/fbec_front¥/gift¥/friends¥/shop_id¥/" . SHOP_ID . "/"); } /** * @When /^友人選択画面で自分贈りを選択する。$/ */ public function selectBuymyself() { $this->mink->getSession()->getPage()->clickLink('buymyself- button'); }
  • 30. な :Context /** * @Then /^自分の住所情報が入 出来る。$/ */ public function assertInputMyAddress() { $this->mink->assertSession()->pageTextContains('※贈り主情報:フリガナ'); $this->mink->assertSession()->pageTextContains('※贈り主情報:メールアドレス'); $this->mink->assertSession()->pageTextContains('※お届け先: 番号'); $this->mink->assertSession()->pageTextContains('※お届け先:都道府県'); $this->mink->assertSession()->pageTextContains('※お届け先:住所'); $this->mink->assertSession()->pageTextContains('※お届け先:電話番号'); } /** * @Given /^FBから引いた情報が補完されている。$/ */ public function assertSetFBinfo() { assertEquals(LAST_NAME_FB, $this->mink->getSession()->getPage()- >findById('last_name')->getValue()); assertEquals(FIRST_NAME_FB, $this->mink->getSession()->getPage()- >findById('first_name')->getValue()); assertEquals(MAIN_EMAIL, $this->mink->getSession()->getPage()->findById('email')- >getValue()); }
  • 31. な :Context /** * @When /^自分の住所情報を入 する。$/ */ public function inputMyAddress() { $this->mink->getSession()->getPage()->fillField('last_name', LAST_NAME_INPUT); $this->mink->getSession()->getPage()->fillField('first_name', FIRST_NAME_INPUT); $this->mink->getSession()->getPage()->fillField('last_name_kana', LAST_NAME_KANA); $this->mink->getSession()->getPage()->fillField('first_name_kana', FIRST_NAME_KANA); $this->mink->getSession()->getPage()->fillField('email', INPUT_EMAIL); $this->mink->getSession()->getPage()->fillField('zip3', ZIP3); $this->mink->getSession()->getPage()->fillField('zip4', ZIP4); $this->mink->getSession()->getPage()->selectFieldOption('prefecture', PREFECTURE); $this->mink->getSession()->getPage()->fillField('address', ADDRESS); $this->mink->getSession()->getPage()->fillField('address2', ADDRESS2); $this->mink->getSession()->getPage()->fillField('phone1', PHONE1); $this->mink->getSession()->getPage()->fillField('phone2', PHONE2); $this->mink->getSession()->getPage()->fillField('phone3', PHONE3); $this->mink->getSession()->getPage()->fillField('note', NOTE_SENDME); } /** * @Given /^確認ボタンを押下する。$/ */ public function pressSubmitShipmentButton() { $this->mink->getSession()->getPage()->clickLink('submit-shipment'); }
  • 32. な :Context /** * @Then /^確認画面に入 卣が勖単される。$/ */ public function assertDisplayInput() { $this->mink->assertSession()->pageTextContains(PRODUCT_NAME); $this->mink->assertSession()->pageTextContains(SHOP_NAME); $this->mink->assertSession()->pageTextContains(PRODUCT_PRICE); $this->mink->assertSession()->pageTextContains(PRODUCT_AND_WRAPPING_PRICE); $this->mink->assertSession()->pageTextContains(PRODUCT_ALL_PRICE); $this->mink->assertSession()->pageTextContains(LAST_NAME_INPUT . ' ' . FIRST_NAME_INPUT); $this->mink->assertSession()->pageTextContains(LAST_NAME_KANA . ' ' . FIRST_NAME_KANA); $this->mink->assertSession()->pageTextContains(INPUT_EMAIL); $this->mink->assertSession()->pageTextContains(ZIP3 . '-' . ZIP4); $this->mink->assertSession()->pageTextContains(PREFECTURE); $this->mink->assertSession()->pageTextContains(ADDRESS); $this->mink->assertSession()->pageTextContains(ADDRESS2); $this->mink->assertSession()->pageTextContains(PHONE1 . '-' . PHONE2 . '-' . PHONE3); $this->mink->assertSession()->pageTextContains(NOTE_SENDME); }
  • 33. な :Context /** * @When /^確定ボタンを押下する。$/ */ public function pressSubmitConfirm() { $this->mink->getSession()->getPage()->clickLink('submit-confirm'); } /** * @Then /^決済画面に遷移する。$/ */ public function assertConfirm2SBPS() { $this->mink->getSession()->wait(5000, 'typeof window.jQuery == "function"'); $this->mink->getSession()->wait(3000, 'window.document.getElementById("#title") > 0'); $this->mink->assertSession()->addressMatches("/¥/f01¥/FepBuyInfoReceive.do/"); } }
  • 35. まとめ • メリット – BDDで「なんちゃって」アジャイルを回避 – behatでテストファーストすることで事前の洗い出しが に • デメリット – スケジュールに追われる場合後回しになりがちで開発 プロセスを徹底するのが難しい – CIサーバに る定期 でアンドン化して 続メンテ ナンスしないと無効なテストケースが増殖
  • 36.