【问题标题】:Create a mock class whose methods return values when instantiated创建一个模拟类,其方法在实例化时返回值
【发布时间】:2013-12-17 20:48:34
【问题描述】:

如何创建一个模拟类(不仅仅是一个模拟对象),其方法在实例化时会返回一个可预测的值?

在下面的代码中,我正在测试一个更大的概念(accounts->preauthorize()),但我需要模拟对象 Lookup 以便我可以获得可预测的测试结果。

我正在使用 PHPUnit 和 CakePHP,如果这很重要的话。这是我的情况:

// The system under test
class Accounts
{
    public function preauthorize()
    {
        $obj = new Lookup();
        $result = $obj->get();
        echo $result; // expect to see 'abc'
        // more work done here
    }
}

// The test file, ideas borrowed from question [13389449][1]
class AccountsTest
{
    $foo = $this->getMockBuilder('nonexistent')
        ->setMockClassName('Lookup')
        ->setMethods(array('get'))
        ->getMock();
    // There is now a mock Lookup class with the method get()
    // However, when my code creates an instance of Lookup and calls get(),
    // it returns NULL. It should return 'abc' instead.

    // I expected this to make my instances return 'abc', but it doesn't. 
    $foo->expects($this->any())
        ->method('get')
        ->will($this->returnValue('abc')); 

    // Now run the test on Accounts->preauthorize()
}

【问题讨论】:

    标签: php unit-testing phpunit


    【解决方案1】:

    这里有几个问题,但主要的一个是你在需要它的方法中实例化你的 Lookup 类。这使得无法模拟。您需要将 Lookup 的实例传递给此方法以解耦依赖关系。

    class Accounts
    {
        public function preauthorize(Lookup $obj)
        {
            $result = $obj->get();
            return $result; // You have to return something here, you can't test echo
        }
    }
    

    现在您可以模拟 Lookup。

    class AccountsTest
    {
        $testLookup = $this->getMockBuilder('Lookup')
            ->getMock();
    
        $testLookup->expects($this->any())
            ->method('get')
            ->will($this->returnValue('abc')); 
    
        $testAccounts = new Accounts();
        $this->assertEquals($testAccounts->preauthorize($testLookup), 'abc');
    }
    

    很遗憾,我无法测试这个测试,但这应该会让你朝着正确的方向前进。

    显然,Lookup 类的单元测试也应该存在。

    您也可能会发现我的answer here 有一些用处。

    【讨论】:

    • 我简化了我的场景,让它更有意义。原来我把它简化了。您确实为我的简化示例提供了一个合理的解决方案,所以谢谢。但实际上 preauthorize() 是一个 CakePHP 控制器,其参数来自客户端,无法容纳 HttpSocket 实例。 (我重命名为“查找”实际上是 CakePHP 实用程序 HttpSocket)。啊。不过,谢谢。
    • 我从未使用过 cake,但我确实听说它就现代框架而言不太好。我要强调的是,不能隔离你的类来对它们进行单元测试是一种代码味道。显然,我无法判断气味是来自您的代码还是来自您的框架。就个人而言,我会仔细研究我的架构。但是,我是一个孤独的开发者,没有人告诉我如何编码或使用什么框架。祝你好运。
    • 重新阅读您的评论。在我遇到的所有框架中,对控制器进行单元测试是不可能的,它不能与其依赖项隔离开来。我通过将尽可能多的逻辑移出控制器并进入模型或服务层来克服这个问题,这样我的控制器就尽可能地瘦了。如果您还没有,这可能是您想要考虑的策略。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-23
    • 1970-01-01
    • 2019-04-11
    • 1970-01-01
    • 2011-02-05
    相关资源
    最近更新 更多