グローバルナビゲーションへ

本文へ

フッターへ

お役立ち情報Blog



実ファイルを使わずにPHPのファイル操作テストをかいてみた

ファイル操作クラスのテストをかく際、どのような方法を思いつくでしょうか?

  • テスト開始時にテスト用ファイルを作成し、テスト終了時に削除する
  • 実際の本番用ファイルでテストを行い、テスト終了時に初期化する

私も上記の方法でテストをかいていましたが、いくつかデメリットがあります。
そのため、今回は仮想ファイルシステムでテストをかいてみようと思います。

筆者の環境
  • CentOS 7
  • PHP 7.3.4

ファイル操作のクラス

まずはテストを行うクラスをかいていきます。
カウンターファイルからカウントを取得しカウントアップするクラスです。 お問い合わせフォームの「お問い合わせNo」の発行に使う想定です。

<?php

class Counter
{
    /**
     * @param string $path
     * @return int
     */
    public function get(string $path): int
    {
        // 読み込み権限がない
        if (!is_readable($path)) {
            throw new RuntimeException('Permission denied to ' . $path);
        }

        return file_get_contents($path);
    }

    /**
     * @param string $path
     */
    public function countUp(string $path): void
    {
        // 書き込み権限がない
        if (!is_writeable($path)) {
            throw new RuntimeException('Permission denied to ' . $path);
        }

        $count = file_get_contents($path);
        file_put_contents($path, (int)$count + 1);
    }
}

実ファイルを使ったテストのメリットデメリット

テストをかく前に実ファイルを使ったテストのメリットデメリットについて触れておきます。

メリット

  • テスト用にライブラリをインストールする必要がない

デメリット

  • 実際の本番用ファイルをテストで編集することは好ましくない
  • テスト内容に応じてファイルを作成しなければならない
  • 共同開発の場合、すべての環境にテスト用ファイルを作成しなければならない
  • テスト上で実ファイルの作成、削除を行うことで予期せぬファイルを消してしまう可能性がある

実ファイルを使わないテスト

仮想ファイルシステムを使ったテストをかいていきます。 今回はPHPのテスティングフレームワークのPHPUnitと仮想ファイルシステムを提供してくれるライブラリのvfsStreamを使います。

PHPUnitとvfsStreamはcomposerでインストールします。
※今回は仮想ファイルシステムでのテストが本題ですのでcomposerのインストールに関しては省略します。公式ドキュメント等に記載されていますのでインストールして下さい。

PHPUnitとvfsStreamのインストール

composer.jsonを記述します。

{
    "require-dev": {
        "phpunit/phpunit": "^8.2",
        "mikey179/vfsstream": "1.0.0"
    }
}

composerでインストールします

$composer install

vfsStreamの動作テスト

まずvfsStreamで仮想ファイルが生成できているかのテストをしてみます。 vfsStreamのhasChildメソッドはファイルの有無をboolean型で返してくれます。

<?php
declare(strict_types=1);

use PHPUnit\Framework\TestCase;
use org\bovigo\vfs\vfsStream;

class VfsStreamTest extends TestCase
{
    private $root;

    public function setUp(): void
    {
        // streamの起点を生成します
        $this->root = vfsStream::setup('root');
    }

    public function createStream(): void
    {
        // setUpで登録した起点にカウンタファイルを生成し、初期値データを登録します
        vfsStream::newFile('counter.dat')->at($this->root)->setContent('1');
    }

    /**
     * @test
     */
    public function 仮想ファイルが作られている(): void
    {
        // 仮想ファイル未作成(falseが返ってくるはず)
        $this->assertFalse($this->root->hasChild('counter.dat'));

        // 仮想ファイルの作成
        $this->createStream();

        // 仮想ファイル作成済(trueが返ってくるはず)
        $this->assertTrue($this->root->hasChild('counter.dat'));
    }
}
$ vendor/bin/phpunit VfsStreamTest.php --testdox
PHPUnit 8.4.2 by Sebastian Bergmann and contributors.

Vfs Stream
 ✔ 仮想ファイルが作られている

Time: 57 ms, Memory: 4.00 MB

OK (1 test, 2 assertions)

仮想ファイルがちゃんと作成されていることが確認できました。

Counterクラスのテスト

本題のCounterクラスのテストをかいていきます。テストの内容は以下になります。

  • ファイルが存在していない場合
  • ファイルの読み込み権限がない場合
  • カウンタファイルの数字が取得できるかどうか
  • ファイルの書き込み権限がない場合
  • カウントアップができるか
<?php
declare(strict_types=1);

require_once './Counter.php';

use PHPUnit\Framework\TestCase;
use org\bovigo\vfs\vfsStream;

class CounterTest extends TestCase
{
    private $counter;
    private $root;

    public function setUp(): void
    {
        $this->counter = new Counter;
        $this->root = vfsStream::setup('root');
    }

    // 仮想ファイルの作成
    public function createStream(int $permission): void
    {
        // 8進数のパーミッションを渡すことで権限設定ができます。
        vfsStream::newFile('counter.dat', $permission)->at($this->root)->setContent('1');
    }

    // 仮想ファイルパスを返す
    public function getPath(): string
    {
        return vfsStream::url('root/counter.dat');
    }

    /**
     * @test
     */
    public function ファイルが存在していない場合(): void
    {
        $this->expectException(RuntimeException::class);
        $this->counter->get($this->getPath());
    }

    /**
     * @test
     */
    public function ファイルの読み込み権限がない場合(): void
    {
        $this->createStream(0100);
        $this->expectException(RuntimeException::class);
        $this->counter->get($this->getPath());
    }

    /**
     * @test
     */
    public function カウンタファイルの数字が取得できる(): void
    {
        $this->createStream(0775);
        $this->assertEquals(1, $this->counter->get($this->getPath()));
    }

    /**
     * @test
     */
    public function ファイルの書き込み権限がない(): void
    {
        $this->createStream(0500);
        $this->expectException(RuntimeException::class);
        $this->counter->countUp($this->getPath());
    }

    /**
     * @test
     */
    public function カウントアップできる(): void
    {
        $this->createStream(0755);
        $this->assertEquals(1, $this->counter->get($this->getPath()));
        $this->counter->countUp($this->getPath());
        $this->assertEquals(2, $this->counter->get($this->getPath()));
        $this->counter->countUp($this->getPath());
        $this->assertEquals(3, $this->counter->get($this->getPath()));
        $this->counter->countUp($this->getPath());
    }
}
$ vendor/bin/phpunit CounterTest.php --testdox
PHPUnit 8.4.2 by Sebastian Bergmann and contributors.

Counter
 ✔ ファイルが存在していない場合
 ✔ ファイルの読み込み権限がない場合
 ✔ カウンタファイルの数字が取得できる
 ✔ ファイルの書き込み権限がない
 ✔ カウントアップできる

Time: 65 ms, Memory: 4.00 MB

OK (5 tests, 7 assertions)

問題なくテストが通りました!

おわりに

vfsStreamには、今回使用した仮想ファイルの作成以外にもディレクトリや階層構造の作成、実ファイルの階層構造をまるっと仮想ファイルシステムに生成等もできるようです。
詳しくはGitHubのvfsStreamのWikiに載っています。

この記事を書いた人

ばね
ばねソリューション事業部 システムエンジニア
東京で2年半エンジニアとしての経験を積み、浜松にUターンの後、アーティスへ入社。
ソリューション事業部のWebエンジニアとして、システムの設計・開発・保守・運用からインフラまで幅広く従事している。
フルスタックエンジニア目指して現在も勉強の日々。車が好き。
この記事のカテゴリ

FOLLOW US

最新の情報をお届けします