模拟在PHP中使用静态方法创建的对象的函数?

模拟在PHP中使用静态方法创建的对象的函数?

问题描述:

I'm using an ORM and the way I get an object of the record is using FooModel::find(1). FooModel has a method I need to mock for testing. How can I do it? (Can't use the PHPUnit mock because that would give me a mocked FooModel that'll not correspond to the record with ID 1.)

Edit

Example:

class FooModel
{
    // ORM model that fetches record from the DB

    public function thisNeedsToBeMocked()
    {
        // some code here that depends on external factors so should be part of unit tests
    }
}

The way I get the record with ID 1 is:

$fooObject = FooModel::find(1);

I need to be able of mock the thisNeedsToBeMocked() of $fooObject that I have after I run the static method find().

我正在使用ORM,而我获取记录对象的方式是使用 FooModel :: 发现(1)代码>。 FooModel code>有一个我需要模拟测试的方法。 我该怎么做? (不能使用PHPUnit mock code>因为这会给我一个模拟的 FooModel code>,它与ID 1 code>的记录不对应。) p>

编辑 h2>

示例: p>

  class FooModel 
 {
 // ORM model 从DB 
 
公共函数中获取记录thisNeedsToBeMocked()
 {
 //这里的一些代码取决于外部因素所以应该是单元测试的一部分
} 
} 
  code>  
 
 

我使用ID 1 code>获取记录的方式是: p>

  $ fooObject = FooModel :: find(  1); 
  code>  pre> 
 
 

我需要能够模拟 $ fooObject code>的 thisNeedsToBeMocked() code> 在我运行 static code>方法 find() code>之后。 p> div>

You have hit on a classic unit testing problem. The class name you are using to call statically is part of the global namespace. That means there is no point in which you can insert a mock. This is no magic you can do here, to solve this problem you will have to make some code changes.

There many ways to solve this and there are many fors and against each of them. I will give you a simple hacker solution. If you would like something different let me know and I will code up an example.

I will also assume that you can't change the ORM from being static class based.

Here is the hacker approach, which is a very bad way of doing things, so you know. Have a variable in your class that is the class name of the ORM class you need to use. This is PHP 5.3 code.

Like so:

<?php

class Bar {
    public static $ormName = 'FooModel';

    public static function doStuff()
    {
        $className = self::$ormName;
        echo $className::find(1), "
";
    }
}

Then in your PHPUnit test create a mock. Get it's class name, set that class name on your test subject. Now your test subject will call your mock.

There are so many ways to some this problem but some how you need a way to not use the class name directly.