CodeIgniter4のフィーチャーテストでモックを使う

この記事は CodeIgniter Advent Calendar 2020 - Qiita の17日目です。まだ、空きがありますので、興味のある方は気軽に参加してください。

CodeIgniter4のフィーチャーテストでデータベースを使う の続きです。

前回はテスト用のデータベースを用意しました。しかし、データベースのテストは必要ないので、モックを使いたいということもあります。

今回はモデルをモックにしてみましょう。

News コントローラの現状

現在のコントローラのコードは以下のようになっています。

app/Controllers/News.php

<?php
namespace App\Controllers;

use App\Models\NewsModel;
use CodeIgniter\Controller;
use CodeIgniter\Exceptions\PageNotFoundException;
use CodeIgniter\HTTP\RequestInterface;
use CodeIgniter\HTTP\ResponseInterface;
use Psr\Log\LoggerInterface;
use Kenjis\CI4Twig\Twig;

class News extends Controller
{
    /**
     * @var Twig
     */
    private $twig;

    public function initController(
        RequestInterface $request,
        ResponseInterface $response,
        LoggerInterface $logger
    ) {
        parent::initController(
            $request,
            $response,
            $logger
        );

        $this->twig = new Twig();
    }

    public function index()
    {
        $model = new NewsModel();

        $data = [
            'news'  => $model->getNews(),
            'title' => 'News archive',
            'main'  => 'news/overview.html',
        ];
//        dd($data);

        return $this->twig->render('news_tmpl.html', $data);
    }

    public function view($slug = null)
    {
        $model = new NewsModel();

        $data['news'] = $model->getNews($slug);

        if (empty($data['news']))
        {
            throw new PageNotFoundException('Cannot find the news item: '. $slug);
        }

        $data['title'] = $data['news']['title'];
        $data['main'] = 'news/view.html';
//        dd($data);

        return $this->twig->render('news_tmpl.html', $data);
    }

    public function create()
    {
        $model = new NewsModel();

        if ($this->request->getMethod() === 'post' && $this->validate([
                'title' => 'required|min_length[3]|max_length[255]',
                'body'  => 'required',
            ]))
        {
            $model->save([
                 'title' => $this->request->getPost('title'),
                 'slug'  => url_title($this->request->getPost('title'), '-', TRUE),
                 'body'  => $this->request->getPost('body'),
            ]);

            $data['main'] = 'news/success.html';

            return $this->twig->render('news_tmpl.html', $data);

        }
        else
        {
            $data = [
                'title' => 'Create a news item',
                'main' => 'news/create.html',
            ];

            return $this->twig->render('news_tmpl.html', $data);
        }
    }
}

$model = new NewsModel(); となっているため、この $model をモックに差し替えることができません。

News コントローラの変更

News コントローラを以下のように変更します。

--- a/app/Controllers/News.php
+++ b/app/Controllers/News.php
@@ -11,6 +11,11 @@ use Kenjis\CI4Twig\Twig;

 class News extends Controller
 {
+    /**
+     * @var NewsModel
+     */
+    private $newsModel;
+
     /**
      * @var Twig
      */
@@ -27,15 +32,15 @@ class News extends Controller
             $logger
         );

+        $this->newsModel = model(NewsModel::class);
+
         $this->twig = new Twig();
     }

     public function index()
     {
-        $model = new NewsModel();
-
         $data = [
-            'news'  => $model->getNews(),
+            'news'  => $this->newsModel->getNews(),
             'title' => 'News archive',
             'main'  => 'news/overview.html',
         ];
@@ -46,9 +51,7 @@ class News extends Controller

     public function view($slug = null)
     {
-        $model = new NewsModel();
-
-        $data['news'] = $model->getNews($slug);
+        $data['news'] = $this->newsModel->getNews($slug);

         if (empty($data['news']))
         {
@@ -64,14 +67,12 @@ class News extends Controller

     public function create()
     {
-        $model = new NewsModel();
-
         if ($this->request->getMethod() === 'post' && $this->validate([
                 'title' => 'required|min_length[3]|max_length[255]',
                 'body'  => 'required',
             ]))
         {
-            $model->save([
+            $this->newsModel->save([
                  'title' => $this->request->getPost('title'),
                  'slug'  => url_title($this->request->getPost('title'), '-', TRUE),
                  'body'  => $this->request->getPost('body'),

new キーワードではなく、CodeIgniter4 の model() 関数を使ってモデルクラスをインスタンス化するように変更しました。

テストコードの追加

モデルをモックに差し替えるテストコードを追加します。 CodeIgniter 4.0.4 でのコードです。

--- a/tests/feature/NewsTest.php
+++ b/tests/feature/NewsTest.php
@@ -2,6 +2,8 @@

 namespace Tests\Feature;

+use App\Models\NewsModel;
+use CodeIgniter\Database\ModelFactory;
 use CodeIgniter\Test\FeatureTestCase;

 class NewsTest extends FeatureTestCase
@@ -21,4 +23,31 @@ class NewsTest extends FeatureTestCase
         $result->assertSee('<title>CodeIgniter Tutorial</title>');
         $result->assertSee('Elvis was sighted at the Podunk internet cafe.');
     }
+
+    public function testGetNewsWithMock()
+    {
+        $newsModel = $this->getMockBuilder(NewsModel::class)
+            ->disableOriginalConstructor()
+            ->onlyMethods(['getNews'])
+            ->getMock();
+        $newsModel->method('getNews')
+            ->willReturn(
+                [
+                    [
+                        'title' => 'Mocked title',
+                        'slug' => 'mocked-title',
+                        'body' => 'Mocked body. Bra, bra, bra...',
+                    ],
+                ]
+            );
+        ModelFactory::injectMock('NewsModel', $newsModel);
+
+        $result = $this->get('news');
+
+        $result->assertStatus(200);
+
+        $result->assertSee('<title>CodeIgniter Tutorial</title>');
+        $result->assertSee('Mocked body. Bra, bra, bra...');
+        $result->assertDontSee('Elvis was sighted at the Podunk internet cafe.');
+    }
 }

CodeIgniter4 の ModelFactory::injectMock() メソッドで作成したモデルを注入します。

データベースにはない本文 Mocked body. Bra, bra, bra... が結果に含まれること、データベースに含まれている Elvis was sighted at the Podunk internet cafe. が結果に含まれないことを確認します。

テストの実行

phpunit を実行します。

$ vendor/bin/phpunit tests/feature/NewsTest.php
PHPUnit 8.5.13 by Sebastian Bergmann and contributors.

Error:         XDEBUG_MODE=coverage or xdebug.mode=coverage has to be set

..                                                                  2 / 2 (100%)

Time: 480 ms, Memory: 10.00 MB

OK (2 tests, 7 assertions)

通りました。

CodeIgniter 4.0.5 以降

開発版の CodeIgniter4 では ModelFactory が廃止予定となり、CodeIgniter 4.0.5 ではテストコードは以下のように書けます。

--- a/tests/feature/NewsTest.php
+++ b/tests/feature/NewsTest.php
@@ -3,7 +3,7 @@
 namespace Tests\Feature;

 use App\Models\NewsModel;
-use CodeIgniter\Database\ModelFactory;
+use CodeIgniter\Config\Factories;
 use CodeIgniter\Test\FeatureTestCase;

 class NewsTest extends FeatureTestCase
@@ -40,7 +40,7 @@ class NewsTest extends FeatureTestCase
                     ],
                 ]
             );
-        ModelFactory::injectMock('NewsModel', $newsModel);
+        Factories::injectMock('models', NewsModel::class, $newsModel);

         $result = $this->get('news');

CodeIgniter4でテストDBをMySQLにする に続きます。

この記事は CodeIgniter Advent Calendar 2020 - Qiita の17日目です。まだ、空きがありますので、興味のある方は気軽に参加してください。

参考

Tags: codeigniter, codeigniter4, testing, database

CodeIgniter4のフィーチャーテストでデータベースを使う

この記事は CodeIgniter Advent Calendar 2020 - Qiita の15日目です。まだ、空きがありますので、興味のある方は気軽に参加してください。

CodeIgniter4のフィーチャーテスト の続きです。

公式チュートリアルの news アプリ のフィーチャーテストを書いてみます。

このアプリはデータベースを使っているため、テスト用のデータベースの準備が必要になります。

テスト時のデータベース設定

PHPUnit でのテスト実行時には、CodeIgniter4 の環境は testing になります。

app/Config/Database.php

また、データベース設定ファイルの $tests が使われます。デフォルトでは、SQLite3 のメモリデータベースになっています。

このように、開発用、本番用データベースとテスト用のデータベースの種類を変えるためには、特定のデータベースに依存したコードが一切ないことが必要になります。まあ、あまり現実的ではないかも知れません。

app/Config/Database.php

...
    /**
     * This database connection is used when
     * running PHPUnit database tests.
     *
     * @var array
     */
    public $tests = [
        'DSN'      => '',
        'hostname' => '127.0.0.1',
        'username' => '',
        'password' => '',
        'database' => ':memory:',
        'DBDriver' => 'SQLite3',
        'DBPrefix' => 'db_',  // Needed to ensure we're working correctly with prefixes live. DO NOT REMOVE FOR CI DEVS
        'pConnect' => false,
        'DBDebug'  => (ENVIRONMENT !== 'production'),
        'cacheOn'  => false,
        'cacheDir' => '',
        'charset'  => 'utf8',
        'DBCollat' => 'utf8_general_ci',
        'swapPre'  => '',
        'encrypt'  => false,
        'compress' => false,
        'strictOn' => false,
        'failover' => [],
        'port'     => 3306,
    ];
...

デフォルトでは、DBPrefixdb_ になっています。

もし、CodeIgniter4 の Query Builder を経由せずに発行されるクエリがある場合は、開発用とテスト用のテーブル名を一致させるように DBPrefix を変更してください。

(2020-12-18 追記) 必要条件の記述をより正確にしました。

--- a/app/Config/Database.php
+++ b/app/Config/Database.php
@@ -64,7 +64,7 @@ class Database extends \CodeIgniter\Database\Config
        'password' => '',
        'database' => ':memory:',
        'DBDriver' => 'SQLite3',
-       'DBPrefix' => 'db_',  // Needed to ensure we're working correctly with prefixes live. DO NOT REMOVE FOR CI DEVS
+       'DBPrefix' => '',
        'pConnect' => false,
        'DBDebug'  => (ENVIRONMENT !== 'production'),
        'cacheOn'  => false,

phpunit.xml

また、phpunit.xml(作成されていない場合は phpunit.xml.dist)にて、PHPUnit 実行時の環境変数 database.tests.databasedatabase.tests.DBDriver が設定されています。

    <php>
        <!-- Directory containing phpunit.xml -->
        <const name="HOMEPATH" value="./"/>

        <!-- Directory containing the Paths config file -->
        <const name="CONFIGPATH" value="./app/Config/"/>

        <!-- Directory containing the front controller (index.php) -->
        <const name="PUBLICPATH" value="./public/"/>

        <!-- Database configuration -->
<!--    <env name="database.tests.hostname" value="localhost"/>  -->
<!--    <env name="database.tests.database" value="tests"/>      -->
<!--    <env name="database.tests.username" value="tests_user"/> -->
<!--    <env name="database.tests.password" value=""/>           -->
<!--    <env name="database.tests.DBDriver" value="MySQLi"/>     -->
<!--    <env name="database.tests.DBPrefix" value="tests_"/>     -->
        <env name="database.tests.database" value=":memory:"/>
        <env name="database.tests.DBDriver" value="SQLite3"/>
    </php>

CodeIgniter4 の設定は、設定ファイルよりも環境変数が優先(設定ファイルの値が環境変数の値で上書き)されます。

また、 phpunit 実行時には、.env より phpunit.xml が先に評価されますので、phpunit.xml が最優先されることになります。

テーブル作成とデータ挿入

とりあえず、以下で作ったマイグレーションとシーダーを使うことにします。

以下のようにテストファイルとプロパティを設定します。

tests/feature/NewsTest.php

<?php

namespace Tests\Feature;

use CodeIgniter\Test\FeatureTestCase;

class NewsTest extends FeatureTestCase
{
    protected $refresh = true;
    protected $namespace = 'App';

    protected $basePath = APPPATH . 'Database';
    protected $seed = 'NewsSeeder';
}

マイグレーション設定

$refresh$namespace はマイグレーションに関する設定です。

$refreshtrue にすると毎回データベースがリフレッシュされます。つまり、PHPUnit の setUp() メソッド実行時に、マイグレーション 0 まで戻して再度マイグレーションを実行します。

$namespace はマイグレーションファイルの名前空間です。Database\Migrations は含めません。指定しない場合は、tests/_support/Database/Migrations 以下を探します。

なお、個人的には、テスト実行時にデータベース構造が変わるわけではないので、毎回マイグレーションをやり直すのは無駄だと思っています。

シーダー設定

$basePath$seed はシーダーに関する設定です。

$basePath はシーダーファイルが配置されているパスを設定します。Seeds ディレクトリは含めません。指定しない場合は、tests/_support/Database/Seeds 以下を探します。

$seed は実行するシーダーファイル名を設定します。

実際には、開発用のシーダーとテスト用のシーダーは分けたほうがいいと思います。

テストコードの作成

テストコードを完成させましょう。

tests/feature/NewsTest.php

<?php

namespace Tests\Feature;

use CodeIgniter\Test\FeatureTestCase;

class NewsTest extends FeatureTestCase
{
    protected $refresh = true;
    protected $namespace = 'App';

    protected $basePath = APPPATH . 'Database';
    protected $seed = 'NewsSeeder';

    public function testGetNews()
    {
        $result = $this->get('news');

        $result->assertStatus(200);

        $result->assertSee('<title>CodeIgniter Tutorial</title>');
        $result->assertSee('Elvis was sighted at the Podunk internet cafe.');
    }
}

$this->get('news')$this->call('get', 'news') と同じ意味です。

データベースにシーダーのデータが挿入されれば、それが表示されるはずです。

テストの実行

phpunit を実行します。

$ vendor/bin/phpunit tests/feature/NewsTest.php
PHPUnit 8.5.13 by Sebastian Bergmann and contributors.

Error:         XDEBUG_MODE=coverage or xdebug.mode=coverage has to be set

.                                                                   1 / 1 (100%)

Time: 418 ms, Memory: 10.00 MB

OK (1 test, 3 assertions)

通りました。

CodeIgniter4のフィーチャーテストでモックを使う に続きます。

この記事は CodeIgniter Advent Calendar 2020 - Qiita の15日目です。まだ、空きがありますので、興味のある方は気軽に参加してください。

参考

Tags: codeigniter, codeigniter4, testing, database

CodeIgniter4のフィーチャーテスト

この記事は CodeIgniter Advent Calendar 2020 - Qiita の14日目です。まだ、空きがありますので、興味のある方は気軽に参加してください。

フィーチャーテストとは?

CodeIgniter4 にはフィーチャーテストの機能が含まれています。

フィーチャーテストは Web アプリへのリクエストサイクル全体をテストする結合テストで、ci-phpunit-test での $this->request() に相当するものです。

Web アプリにリクエストを擬似的に送信し、その結果についてテストするということで、直感的にわかりやすいものです。

テストのための設定

プロジェクトルートに PHPUnit の設定ファイル phpunit.xml.dist があります。これを、phpunit.xml としてコピーし、自分の環境に合うように変更します。

app.baseURL.env で設定していますので削除します。

--- phpunit.xml.dist    2020-12-14 09:16:19.000000000 +0900
+++ phpunit.xml 2020-12-14 09:16:19.000000000 +0900
@@ -36,8 +36,6 @@
    </logging>

    <php>
-       <server name="app.baseURL" value="http://example.com"/>
-
        <!-- Directory containing phpunit.xml -->
        <const name="HOMEPATH" value="./"/>

phpunit.xml はプロジェクトで共有するので、git add -f で追加してバージョン管理しましょう。

テストファイルの作成

Welcome ページをリクエストするテストを作成してみましょう。

tests/feature フォルダを作成し、tests/feature/HomeTest.php を作成します。

tests/feature/HomeTest.php

<?php

namespace Tests\Feature;

use CodeIgniter\Test\FeatureTestCase;

class HomeTest extends FeatureTestCase
{
    public function testGetIndex()
    {
        $result = $this->call('get', '/');
        dd($result);
    }
}

GET メソッドでパス / に、リクエストを送信するコードです。

dd() 関数で、取得した結果を表示してみます。

返り値の確認

テストを実行してみましょう。

$ vendor/bin/phpunit tests/feature/HomeTest.php
PHPUnit 8.5.13 by Sebastian Bergmann and contributors.

Error:         XDEBUG_MODE=coverage or xdebug.mode=coverage has to be set


┌──────────────────────────────────────────────────────────────────────────────┐
│ $result                                                                      │
└──────────────────────────────────────────────────────────────────────────────┘
CodeIgniter\Test\FeatureResponse (45) (
    public 'response' -> CodeIgniter\HTTP\Response (17) (
        protected 'reason' -> string (2) "OK"
        protected 'statusCode' -> integer 200
        protected 'CSPEnabled' -> boolean false
        public 'CSP' -> CodeIgniter\HTTP\ContentSecurityPolicy (22) (
            protected 'baseURI' -> null
            protected 'childSrc' -> string (4) "self"
            protected 'connectSrc' -> string (4) "self"
            protected 'defaultSrc' -> null
            protected 'fontSrc' -> null
            protected 'formAction' -> string (4) "self"
            protected 'frameAncestors' -> null
            protected 'imageSrc' -> string (4) "self"
            protected 'mediaSrc' -> null
            protected 'objectSrc' -> string (4) "self"
            protected 'pluginTypes' -> null
            protected 'reportURI' -> null
            protected 'sandbox' -> null
            protected 'scriptSrc' -> string (4) "self"
            protected 'styleSrc' -> string (4) "self"
            protected 'manifestSrc' -> null
            protected 'upgradeInsecureRequests' -> boolean false
            protected 'reportOnly' -> boolean false
            protected 'validSources' -> array (4) [
                0 => string (4) "self"
                1 => string (4) "none"
                2 => string (13) "unsafe-inline"
                3 => string (11) "unsafe-eval"
            ]
            protected 'nonces' -> array (0) []
            protected 'tempHeaders' -> array (0) []
            protected 'reportOnlyHeaders' -> array (0) []
        )
        protected 'cookiePrefix' -> string (0) ""
        protected 'cookieDomain' -> string (0) ""
        protected 'cookiePath' -> string (1) "/"
        protected 'cookieSecure' -> boolean false
        protected 'cookieHTTPOnly' -> boolean false
        protected 'cookies' -> array (0) []
        protected 'pretend' -> boolean false
        protected 'bodyFormat' -> string (4) "html"
        protected 'headers' -> array (2) [
            'Cache-control' => CodeIgniter\HTTP\Header (2) (
                protected 'name' -> string (13) "Cache-control"
                protected 'value' -> array (3) [
                    0 => string (8) "no-store"
                    1 => string (9) "max-age=0"
                    2 => string (8) "no-cache"
                ]
            )
            'Content-Type' => CodeIgniter\HTTP\Header (2) (
                protected 'name' -> string (12) "Content-Type"
                protected 'value' -> string (24) "text/html; charset=UTF-8"
            )
        ]
        protected 'headerMap' -> array (2) [
            'cache-control' => string (13) "Cache-control"
            'content-type' => string (12) "Content-Type"
        ]
        protected 'protocolVersion' -> string (3) "1.1"
        protected 'validProtocolVersions' -> array (3) [
            0 => string (3) "1.0"
            1 => string (3) "1.1"
            2 => string (1) "2"
        ]
        protected 'body' -> string (18385) "<!-- DEBUG-VIEW START 1 APPPATH/Config/../Views/welcome_message.php -->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Welcome to CodeIgniter 4!</title>
    <meta name="description" content="The small framework with powerful features">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="shortcut icon" type="image/png" href="/favicon.ico"/>

    <!-- STYLES -->

    <style {csp-style-nonce}>
        ...
    </style>
</head>
<body>

<!-- HEADER: MENU + HEROE SECTION -->
<header>

    <div class="menu">
        <ul>
            <li class="logo"><a href="https://codeigniter.com" target="_blank"><img height="44" title="CodeIgniter Logo"
                                        alt="Visit CodeIgniter.com official website!"
                                        src="data:image/png;base64,..."></a>
            </li>
            <li class="menu-toggle">
                <button onclick="toggleMenu();">&#9776;</button>
            </li>
            <li class="menu-item hidden"><a href="#">Home</a></li>
            <li class="menu-item hidden"><a href="https://codeigniter4.github.io/userguide/" target="_blank">Docs</a>
            </li>
            <li class="menu-item hidden"><a href="https://forum.codeigniter.com/" target="_blank">Community</a></li>
            <li class="menu-item hidden"><a
                    href="https://github.com/codeigniter4/CodeIgniter4/blob/master/CONTRIBUTING.md" target="_blank">Contribute</a>
            </li>
        </ul>
    </div>

    <div class="heroe">

        <h1>Welcome to CodeIgniter 4.0.4</h1>

        <h2>The small framework with powerful features</h2>

    </div>

</header>

<!-- CONTENT -->

<section>

    <h1>About this page</h1>

    <p>The page you are looking at is being generated dynamically by CodeIgniter.</p>

    <p>If you would like to edit this page you will find it located at:</p>

    <pre><code>app/Views/welcome_message.php</code></pre>

    <p>The corresponding controller for this page can be found at:</p>

    <pre><code>app/Controllers/Home.php</code></pre>

</section>

<div class="further">

    <section>

        <h1>Go further</h1>

        <h2>
            <svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'>...</svg>
            Learn
        </h2>

        <p>The User Guide contains an introduction, tutorial, a number of "how to"
            guides, and then reference documentation for the components that make up
            the framework. Check the <a href="https://codeigniter4.github.io/userguide"
            target="_blank">User Guide</a> !</p>

        <h2>
            <svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'>...</svg>
            Discuss
        </h2>

        <p>CodeIgniter is a community-developed open source project, with several
             venues for the community members to gather and exchange ideas. View all
             the threads on <a href="https://forum.codeigniter.com/"
             target="_blank">CodeIgniter's forum</a>, or <a href="https://codeigniterchat.slack.com/"
             target="_blank">chat on Slack</a> !</p>

        <h2>
             <svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'>...</svg>
             Contribute
        </h2>

        <p>CodeIgniter is a community driven project and accepts contributions
             of code and documentation from the community. Why not
             <a href="https://codeigniter.com/en/contribute" target="_blank">
             join us</a> ?</p>

    </section>

</div>

<!-- FOOTER: DEBUG INFO + COPYRIGHTS -->

<footer>
    <div class="environment">

        <p>Page rendered in 0.0181 seconds</p>

        <p>Environment: testing</p>

    </div>

    <div class="copyrights">

        <p>&copy; 2020 CodeIgniter Foundation. CodeIgniter is open source project released under the MIT
            open source licence.</p>

    </div>

</footer>

...

</body>
</html>

<!-- DEBUG-VIEW ENDED 1 APPPATH/Config/../Views/welcome_message.php -->
"
    )
    protected 'domParser' -> CodeIgniter\Test\DOMParser (1) (
        protected 'dom' -> DOMDocument (0) ()
    )
    protected 'backupGlobals' -> null
    protected 'backupGlobalsBlacklist' -> array (0) []
    protected 'backupStaticAttributes' -> null
    protected 'backupStaticAttributesBlacklist' -> array (0) []
    protected 'runTestInSeparateProcess' -> null
    protected 'preserveGlobalState' -> boolean true
    private 'runClassInSeparateProcess' -> null
    private 'inIsolation' -> boolean false
    private 'data' -> null
    private 'dataName' -> null
    private 'expectedException' -> null
    private 'expectedExceptionMessage' -> null
    private 'expectedExceptionMessageRegExp' -> null
    private 'expectedExceptionCode' -> null
    private 'name' -> string (0) ""
    private 'dependencies' -> array (0) []
    private 'dependencyInput' -> array (0) []
    private 'iniSettings' -> array (0) []
    private 'locale' -> array (0) []
    private 'mockObjects' -> array (0) []
    private 'mockObjectGenerator' -> null
    private 'status' -> integer -1
    private 'statusMessage' -> string (0) ""
    private 'numAssertions' -> integer 0
    private 'result' -> null
    private 'testResult' -> null
    private 'output' -> string (0) ""
    private 'outputExpectedRegex' -> null
    private 'outputExpectedString' -> null
    private 'outputCallback' -> boolean false
    private 'outputBufferingActive' -> boolean false
    private 'outputBufferingLevel' -> null
    private 'outputRetrievedForAssertion' -> boolean false
    private 'snapshot' -> null
    private 'prophet' -> null
    private 'beStrictAboutChangesToGlobalState' -> boolean false
    private 'registerMockObjectsFromTestArgumentsRecursively' -> boolean false
    private 'warnings' -> array (0) []
    private 'groups' -> array (0) []
    private 'doesNotPerformAssertions' -> boolean false
    private 'customComparators' -> array (0) []
    private 'doubledTypes' -> array (0) []
    private 'deprecatedExpectExceptionMessageRegExpUsed' -> boolean false
)
════════════════════════════════════════════════════════════════════════════════
Called from <ROOT>/tests/feature/HomeTest.php:12 [dd()]

$this->call()CodeIgniter\Test\FeatureResponse オブジェクトを返す。その response プロパティに CodeIgniter\HTTP\Response オブジェクトが含まれていることがわかります。

テストコードの作成

流れがわかりましたので、テストコードを作成していきましょう。

HTTP ステータスやレスポンスに含まれる文字列を検証します。

tests/feature/HomeTest.php

<?php

namespace Tests\Feature;

use CodeIgniter\Test\FeatureTestCase;

class HomeTest extends FeatureTestCase
{
    public function testGetIndex()
    {
        $result = $this->call('get', '/');
//        dd($result);

        $result->assertStatus(200);

        $result->assertSee('<title>Welcome to CodeIgniter 4!</title>');
        $result->assertSee('Environment: testing');
    }
}

ステータスコードが 200 であることと、<title> タグの文字列、それと環境が testing になっていることを確認します。

テストの実行

phpunit を実行します。

$ vendor/bin/phpunit tests/feature/HomeTest.php
PHPUnit 8.5.13 by Sebastian Bergmann and contributors.

Error:         XDEBUG_MODE=coverage or xdebug.mode=coverage has to be set

.                                                                   1 / 1 (100%)

Time: 302 ms, Memory: 10.00 MB

OK (1 test, 3 assertions)

通りました。

テスト実行中は CodeIgniter4 の環境が testing になっていることも確認できました。

CodeIgniter4のフィーチャーテストでデータベースを使う に続きます。

この記事は CodeIgniter Advent Calendar 2020 - Qiita の14日目です。まだ、空きがありますので、興味のある方は気軽に参加してください。

参考

Tags: codeigniter, codeigniter4, testing