PHP
PHPUnit
mockery

MockeryでoverloadをつかってテストしたらCould not load mockとか言われる件

More than 1 year has passed since last update.

問題

Mockeryでoverloadを使うことで、比較的依存度の高いクラスをテストすることができるようになります。Mockeryについては下の記事を参照ください。

Mockeryを使って Hoge クラスをテストする HogeTest クラスを作成し、phpunitを実行したところ、次のようなエラーに出くわしました。

1) path\to\HogeTest::fooTest
Mockery\Exception\RuntimeException: Could not load mock path\to\ClassUsedInTest, class already exists

解決策

メソッドの前に以下のアノテーションを追加する。アノテーションの説明はドキュメントを読んでね

HogeTest.php
class HogeTest extends \TestCase
{
    /**
     * @runInSeparateProcess
     * @preserveGlobalState disabled
     */
    public function fooTest()
    {
        // Hogeクラスで利用している、ClassUsedInTestクラスのモックを作る
        $mock = \Mockery::mock('overload:' . \path\to\ClassUsedInTest::class);
        // 以下、テストの具体的な処理が続く...
    }
}

原因

This works best when using an autoloader in your tests. If any code is encountered that uses a class that hasn’t yet been loaded, it invokes the autoloader to load the class. Mockery allows you to overload this behavior by injecting a class with the same name, so that when the class is encountered in your code, it is already loaded, and the autoloader doesn’t try to load your real class. If you’re usingComposer’s autoloader with your tests, you’re all set.

This is not without some problems, though. If previous tests have already autoloaded the class, then Mockery won’t be able to inject a class with the same name. This will result in an error:

引用元:http://devzone.zend.com/6073/mocking-hard-dependencies-in-legacy-code/

日本語でおk

Mockeryでoverloadすると、オートローディングする際にDIすることができます。でも、もし前のテストがモック対象のクラスをオートロードしていると(つまり、テスト対象のクラスをnewしたりしていると)DIに失敗してしまいます。

つまり、 HogeTest クラスで利用している ClassUsedInTest クラスを、どこかこれ以外のテストで既に使っていることが原因です。例えば ClassUsedInTest クラス自体に対するテストでnew ClassUsedInTestをしているかも?

この状況は、Staticメソッドをモックする際にもおこります。