PHP 中的单元测试数据存储

发布于 2024-07-27 23:23:34 字数 920 浏览 7 评论 0原文

我正在使用 PHPUnit,但发现很难让它为用作数据存储的对象创建良好的模拟和存根。

示例:

class urlDisplayer {
    private $storage;
    public function __construct(IUrlStorage $storage) { $this->storage = $storage; }
    public function displayUrl($name) {}
    public function displayLatestUrls($count) {}
}

interface IUrlStorage {
    public function addUrl($name, $url);
    public function getUrl($name);
}

class MysqlUrlStorage implements IUrlStorage {
    // saves and retrieves from database
}

class NonPersistentStorage implements IUrlStorage {
    // just stores for this request
}

例如,如何让 PHPUnit 存根在具有不同 $name 的两次调用中返回多个可能值?

编辑:示例测试:

public function testUrlDisplayerDisplaysLatestUrls {
    // get mock storage and have it return latest x urls so I can test whether
    // UrlDisplayer really shows the latest x
}

在此测试中,模拟应该返回多个 url,但是在文档中我只介绍如何返回一个值。

I'm using PHPUnit but find it difficult to make it create good mocks and stubs for objects used as datastore.

Example:

class urlDisplayer {
    private $storage;
    public function __construct(IUrlStorage $storage) { $this->storage = $storage; }
    public function displayUrl($name) {}
    public function displayLatestUrls($count) {}
}

interface IUrlStorage {
    public function addUrl($name, $url);
    public function getUrl($name);
}

class MysqlUrlStorage implements IUrlStorage {
    // saves and retrieves from database
}

class NonPersistentStorage implements IUrlStorage {
    // just stores for this request
}

Eg how to have PHPUnit stubs returning more than one possible value on two calls with different $names?

Edit: example test:

public function testUrlDisplayerDisplaysLatestUrls {
    // get mock storage and have it return latest x urls so I can test whether
    // UrlDisplayer really shows the latest x
}

In this test the mock should return a number of urls, however in the documentation I only how to return one value.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

一个人的旅程 2024-08-03 23:23:34

你的问题不是很清楚 - 但我假设你问如何使用 phpunit 的模拟对象在不同情况下返回不同的值?

PHPUnit 的模拟类允许您指定自定义函数(即:回调函数/方法)——它的功能实际上是无限的。

在下面的示例中,我创建了一个模拟 IUrlStorage 类,每次调用时都会返回其存储中的下一个 url。

public function setUp()
{
    parent::setUp();
    $this->fixture = new UrlDisplayer(); //change this to however you create your object

    //Create a list of expected URLs for testing across all test cases
    $this->expectedUrls = array(
          'key1' => 'http://www.example.com/url1/'
        , 'key2' => 'http://www.example.net/url2/'
        , 'key3' => 'http://www.example.com/url3/'
    );
}

public function testUrlDisplayerDisplaysLatestUrls {
    //Init        
    $mockStorage = $this->getMock('IUrlStorage');
    $mockStorage->expects($this->any())
        ->method('getUrl')
        ->will( $this->returnCallback(array($this, 'mockgetUrl')) );

    reset($this->expectedUrls); //reset array before testing

    //Actual Tests
    $this->assertGreaterThan(0, count($this->expectedUrls));
    foreach ( $this->expectedUrls as $key => $expected ) {
        $actual = $this->fixture->displayUrl($key);
        $this->assertEquals($expected, $actual);
    }
}

public function mockGetUrl($name)
{
    $value = current($this->expectedUrls);
    next($this->expectedUrls);

    //Return null instead of false when end of array is reached
    return ($value === false) ? null : $value;
}

或者,有时简单地创建一个模拟必要功能的真实类会更容易。 对于定义良好且较小的接口来说,这尤其容易。

在这种特定情况下,我建议改用下面的类:

class MockStorage implements IUrlStorage
{
    protected $urls = array();

    public function addUrl($name, $url)
    {
        $this->urls[$name] = $url;
    }

    public function getUrl($name)
    {
        if ( isset($this->urls[$name]) ) {
            return $this->urls[$name];
        }
        return null;
    }
}
?>

然后在单元测试类中,您只需实例化您的装置,如下所示:

public function setUp() {
   $mockStorage = new MockStorage();

   //Add as many expected URLs you want to test for
   $mockStorage->addUrl('name1', 'http://example.com');
   //etc...

   $this->fixture = new UrlDisplayer($mockStorage);
}

Your question is not very clear - but I assume you are asking how to use phpunit's mock objects to return a different value in different situations?

PHPUnit's mock classes allow you specify a custom function (ie: a callback function/method) - which is practically unlimited in what it can do.

In the below example, I created a mock IUrlStorage class that will return the next url in its storage each time it is called.

public function setUp()
{
    parent::setUp();
    $this->fixture = new UrlDisplayer(); //change this to however you create your object

    //Create a list of expected URLs for testing across all test cases
    $this->expectedUrls = array(
          'key1' => 'http://www.example.com/url1/'
        , 'key2' => 'http://www.example.net/url2/'
        , 'key3' => 'http://www.example.com/url3/'
    );
}

public function testUrlDisplayerDisplaysLatestUrls {
    //Init        
    $mockStorage = $this->getMock('IUrlStorage');
    $mockStorage->expects($this->any())
        ->method('getUrl')
        ->will( $this->returnCallback(array($this, 'mockgetUrl')) );

    reset($this->expectedUrls); //reset array before testing

    //Actual Tests
    $this->assertGreaterThan(0, count($this->expectedUrls));
    foreach ( $this->expectedUrls as $key => $expected ) {
        $actual = $this->fixture->displayUrl($key);
        $this->assertEquals($expected, $actual);
    }
}

public function mockGetUrl($name)
{
    $value = current($this->expectedUrls);
    next($this->expectedUrls);

    //Return null instead of false when end of array is reached
    return ($value === false) ? null : $value;
}

Alternatively, sometimes it is easier to simply create a real class that mocks up the necessary functionality. This is especially easy with well defined and small interfaces.

In this specific case, I would suggest using the below class instead:

class MockStorage implements IUrlStorage
{
    protected $urls = array();

    public function addUrl($name, $url)
    {
        $this->urls[$name] = $url;
    }

    public function getUrl($name)
    {
        if ( isset($this->urls[$name]) ) {
            return $this->urls[$name];
        }
        return null;
    }
}
?>

Then in your unit test class you simply instantiate your fixture like below:

public function setUp() {
   $mockStorage = new MockStorage();

   //Add as many expected URLs you want to test for
   $mockStorage->addUrl('name1', 'http://example.com');
   //etc...

   $this->fixture = new UrlDisplayer($mockStorage);
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文