behat

導入

install

$ composer require behat/behat behat/mink-extension behat/mink-goutte-driver
Using version ^3.4 for behat/behat
Using version ^2.3 for behat/mink-extension
Using version ^1.2 for behat/mink-goutte-driver
./composer.json has been created
Loading composer repositories with package information
Updating dependencies (including require-dev)


...
Writing lock file
Generating autoload files

もし、./binの下に置きたければ

composer.json

{
    "config": {
        "bin-dir": "bin/"
    }
}

を置いておくといいかも。とりあえず本項はvendorの下に入ったものとする。

確認

まずinit

$ ./vendor/bin/behat --init
+d features - place your *.feature files here
+d features/bootstrap - place your context classes here
+f features/bootstrap/FeatureContext.php - place your definitions, transformations and hooks here

こんなのが出来る

$ find features/
features/
features/bootstrap
features/bootstrap/FeatureContext.php

features/000_tests.feature

などを作成し、以下のように

Feature: test

    Scenario: empty

実行

$ ./vendor/bin/behat
Feature: test

  Scenario: empty # features/000_tests.feature:3

No scenarios
ステップがありません
0m0.01s (7.85Mb)

利用できるテストケースは-dlオプションで確認できるのだが

$ ./vendor/bin/behat -dl    

ここでテストケースをインポートする設定を書く。

behat.yml

default:
  suites:
    default:
      path: %paths.base%/features
      contexts:
        - FeatureContext
        - Behat\MinkExtension\Context\MinkContext # <- これ

LANG=Cを付けて実行してみる

$ LANG=C ./vendor/bin/behat -dl
default | Given /^(?:|I )am on (?:|the )homepage$/
default | When /^(?:|I )go to (?:|the )homepage$/
default | Given /^(?:|I )am on "(?P<page>[^"]+)"$/
default | When /^(?:|I )go to "(?P<page>[^"]+)"$/
default | When /^(?:|I )reload the page$/
default | When /^(?:|I )move backward one page$/
default | When /^(?:|I )move forward one page$/
default | When /^(?:|I )press "(?P<button>(?:[^"]|\\")*)"$/
default | When /^(?:|I )follow "(?P<link>(?:[^"]|\\")*)"$/
default | When /^(?:|I )fill in "(?P<field>(?:[^"]|\\")*)" with "(?P<value>(?:[^"]|\\")*)"$/
default | When /^(?:|I )fill in "(?P<field>(?:[^"]|\\")*)" with:$/
default | When /^(?:|I )fill in "(?P<value>(?:[^"]|\\")*)" for "(?P<field>(?:[^"]|\\")*)"$/
default | When /^(?:|I )fill in the following:$/
default | When /^(?:|I )select "(?P<option>(?:[^"]|\\")*)" from "(?P<select>(?:[^"]|\\")*)"$/
default | When /^(?:|I )additionally select "(?P<option>(?:[^"]|\\")*)" from "(?P<select>(?:[^"]|\\")*)"$/
default | When /^(?:|I )check "(?P<option>(?:[^"]|\\")*)"$/
default | When /^(?:|I )uncheck "(?P<option>(?:[^"]|\\")*)"$/
default | When /^(?:|I )attach the file "(?P<path>[^"]*)" to "(?P<field>(?:[^"]|\\")*)"$/
default | Then /^(?:|I )should be on "(?P<page>[^"]+)"$/
default | Then /^(?:|I )should be on (?:|the )homepage$/
default | Then /^the (?i)url(?-i) should match (?P<pattern>"(?:[^"]|\\")*")$/
default | Then /^the response status code should be (?P<code>\d+)$/
default | Then /^the response status code should not be (?P<code>\d+)$/
default | Then /^(?:|I )should see "(?P<text>(?:[^"]|\\")*)"$/
default | Then /^(?:|I )should not see "(?P<text>(?:[^"]|\\")*)"$/
default | Then /^(?:|I )should see text matching (?P<pattern>"(?:[^"]|\\")*")$/
default | Then /^(?:|I )should not see text matching (?P<pattern>"(?:[^"]|\\")*")$/
default | Then /^the response should contain "(?P<text>(?:[^"]|\\")*)"$/
default | Then /^the response should not contain "(?P<text>(?:[^"]|\\")*)"$/
default | Then /^(?:|I )should see "(?P<text>(?:[^"]|\\")*)" in the "(?P<element>[^"]*)" element$/
default | Then /^(?:|I )should not see "(?P<text>(?:[^"]|\\")*)" in the "(?P<element>[^"]*)" element$/
default | Then /^the "(?P<element>[^"]*)" element should contain "(?P<value>(?:[^"]|\\")*)"$/
default | Then /^the "(?P<element>[^"]*)" element should not contain "(?P<value>(?:[^"]|\\")*)"$/
default | Then /^(?:|I )should see an? "(?P<element>[^"]*)" element$/
default | Then /^(?:|I )should not see an? "(?P<element>[^"]*)" element$/
default | Then /^the "(?P<field>(?:[^"]|\\")*)" field should contain "(?P<value>(?:[^"]|\\")*)"$/
default | Then /^the "(?P<field>(?:[^"]|\\")*)" field should not contain "(?P<value>(?:[^"]|\\")*)"$/
default | Then /^(?:|I )should see (?P<num>\d+) "(?P<element>[^"]*)" elements?$/
default | Then /^the "(?P<checkbox>(?:[^"]|\\")*)" checkbox should be checked$/
default | Then /^the "(?P<checkbox>(?:[^"]|\\")*)" checkbox is checked$/
default | Then /^the checkbox "(?P<checkbox>(?:[^"]|\\")*)" (?:is|should be) checked$/
default | Then /^the "(?P<checkbox>(?:[^"]|\\")*)" checkbox should (?:be unchecked|not be checked)$/
default | Then /^the "(?P<checkbox>(?:[^"]|\\")*)" checkbox is (?:unchecked|not checked)$/
default | Then /^the checkbox "(?P<checkbox>(?:[^"]|\\")*)" should (?:be unchecked|not be checked)$/
default | Then /^the checkbox "(?P<checkbox>(?:[^"]|\\")*)" is (?:unchecked|not checked)$/
default | Then /^print current URL$/
default | Then /^print last response$/
default | Then /^show last response$/

基本

localhostのhtmlを取ってみよう。localhostにアクセスできれなければ他でもよいが。

features/000_tests.feature

Feature: test

    Scenario: print response
        Given I am on "/"
        Then print last response

実行する

$ ./vendor/bin/behat        Feature: test

  Scenario: print response   # features/000_tests.feature:3
    Given I am on "/"        # Behat\MinkExtension\Context\MinkContext::visit()
      Mink instance has not been set on Mink context class. Have you enabled the Mink Extension? (RuntimeException)
    Then print last response # Behat\MinkExtension\Context\MinkContext::printLastResponse()

--- 失敗した シナリオ:

    features/000_tests.feature:3

1 個のシナリオ (1 個失敗)
2 個のステップ (1 個失敗, 1 個スキップ)
0m0.02s (8.25Mb)

まあいろいろ失敗するのでbehat.ymlを拡張する

default:
  suites:
    default:
      path: %paths.base%/features
      contexts:
        - FeatureContext
        - Behat\MinkExtension\Context\MinkContext
  # ↓ ここから
  extensions:
    Behat\MinkExtension:
      base_url: http://localhost
      sessions:
        default:
          goutte: ~

実行して適当に成功する事を確認する。

拡張

上記のインポートされたテストケースでも大抵いいのだが、例えばn秒待ってから次、といったようなものがない。これを実装してみよう。

シナリオ

features/000_tests.featureを以下のように改造してみる

Feature: test

    Scenario: print response
        Given I am on "/"
        And I wait 5
        Then print last response

これで実行すると、もちろんI waitというのが定義されていないので、こんな具合で止まる

Feature: test

  Scenario: print response   # features/000_tests.feature:3
    Given I am on "/"        # Behat\MinkExtension\Context\MinkContext::visit()
    And I wait 5
    Then print last response # Behat\MinkExtension\Context\MinkContext::printLastResponse()

1 scenario (1 undefined)
3 steps (1 passed, 1 undefined, 1 skipped)
0m0.03s (10.56Mb)

 >> default suite has undefined steps. Please choose the context to generate snippets:

  [0] None
  [1] FeatureContext
  [2] Behat\MinkExtension\Context\MinkContext
 > 

ここで、適当に2にすると

--- Behat\MinkExtension\Context\MinkContext has missing steps. Define them with these snippets:

    /**
     * @Given I wait :arg1
     */
    public function iWait($arg1)
    {
        throw new PendingException();
    }

という指示が出るため、これを記述してみよう

features/bootstrap/FeatureContext.php

    /**
     * @Given I wait :arg1
     */
    public function iWait($time)
    {
        sleep($time);
    }

コメントまで含んで書く事。でないと認識してくれない。

これで-dlを付けると

$ LANG=C ./vendor/bin/behat -dl
default | Given I wait :arg1
...

このように定義されたものが見えるはずだ。実行してみる実際5秒待つ事になるはず。

拡張を使ってテスト

こんなダミーのphpを作っておくとする。

<?php
print_r($_GET);
print_r($_POST);
print_r($_COOKIE);

こんなシナリオを用意する

Feature: request test

    Scenario: requesting test script
        Given I am on "/"
           And I request test script

適当に実行する。

$ ./vendor/bin/behat features/001_request_test.feature 
Feature: request test

  Scenario: requesting test script # features/001_request_test.feature:3
    Given I am on "/"              # Behat\MinkExtension\Context\MinkContext::visit()
    And I requestTestScript
    Then print last response       # Behat\MinkExtension\Context\MinkContext::printLastResponse()

1 個のシナリオ (1 個未定義)
3 個のステップ (1 個成功, 1 個未定義, 1 個スキップ)
0m0.03s (10.71Mb)

 >> default suite has undefined steps. Please choose the context to generate snippets:

  [0] None
  [1] FeatureContext
  [2] Behat\MinkExtension\Context\MinkContext
 > 2

--- Behat\MinkExtension\Context\MinkContext のステップが見つかりません。 次のスニペットで定義できます:

    /**
     * @Given I request test script
     */
    public function iRequestTestScript()
    {
        throw new PendingException();
    }

ここまでは同じ。引数を取ってもいいが、埋めこんでしまう。

    /**
     * @Given I requestTestScript
     */
    public function iRequesttestscript()
    {
        readfile("http://localhost/test.php?a=b");
    }

実行

$ ./vendor/bin/behat features/001_request_test.feature 
Feature: request test

  Scenario: requesting test script # features/001_request_test.feature:3
    Given I am on "/"              # Behat\MinkExtension\Context\MinkContext::visit()
    And I request test script        # FeatureContext::iRequesttestscript()
      │ Array
      │ (
      │     [a] => b
      │ )
      │ Array
      │ (
      │ )
      │ Array
      │ (
      │ )
      │ 

セッションを維持する

上記の例はあまり意味がなくて、この手のものはセッションを維持できるかが重要になる。たとえばテストスクリプトをこのように変更する。よくあるセッションカウンターだが。

<?php
session_start();
if (!isset($_SESSION['cnt'])) {
    $_SESSION['cnt'] = 0;
}
$_SESSION['cnt']++;
 
echo $_SESSION['cnt']."\n"

minkを使うとセッションを維持できるので、以下のようなテストが通るはずだ。(localhost/test.phpにあるものとする)

Feature: request test

    Scenario: requesting test script
        Given I am on "/test.php"
           Then I should see "1"
        Given I am on "/test.php"
           Then I should see "2"
        Given I am on "/test.php"
           Then I should see "3"
Feature: request test

  Scenario: requesting test script # features/002_session_test.feature:3
    Given I am on "/test.php"      # Behat\MinkExtension\Context\MinkContext::visit()
    Then I should see "1"          # Behat\MinkExtension\Context\MinkContext::assertPageContainsText()
    Given I am on "/test.php"      # Behat\MinkExtension\Context\MinkContext::visit()
    Then I should see "2"          # Behat\MinkExtension\Context\MinkContext::assertPageContainsText()
    Given I am on "/test.php"      # Behat\MinkExtension\Context\MinkContext::visit()
    Then I should see "3"  

もちろん、このテストをこのようにすると

Feature: request test

    Scenario: requesting test script
        Given I am on "/test.php"
           And I request test script
           And I request test script
           And I request test script

このように

  Scenario: requesting test script # features/002_session_test.feature:3
    Given I am on "/test.php"      # Behat\MinkExtension\Context\MinkContext::visit()
    And I request test script      # FeatureContext::iRequestTestScript()
      │ 1
      │ 
    And I request test script      # FeatureContext::iRequestTestScript()
      │ 1
      │ 
    And I request test script      # FeatureContext::iRequestTestScript()
      │ 1
      │ 

セッションは引きつがれない。これを取り出すには中で動いているminkの「セッション」(phpのセッションではない、まぎらわしいが)を取り出さないといけない。

behat.txt · 最終更新: 2017/11/27 22:20 by admin
www.chimeric.de Creative Commons License Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0