PHPUnit - 同一类的多个存根

发布于 2024-08-26 07:45:51 字数 1299 浏览 2 评论 0原文

我正在为类 Foo 构建单元测试,并且我对单元测试相当陌生。

我的类的一个关键组件是 BarCollection 的实例,它包含许多 Bar 对象。 Foo 中的一个方法循环访问集合,并对集合中的每个 Bar 对象调用几个方法。我想使用存根对象为我的测试类生成一系列响应。如何使 Bar 存根类在迭代时返回不同的值?我正在尝试按照以下方式做一些事情:

$stubs = array();
foreach ($array as $value) {
    $barStub = $this->getMock('Bar');
    $barStub->expects($this->any())
            ->method('GetValue')
            ->will($this->returnValue($value));
    $stubs[] = $barStub;
}
// populate stubs into `Foo`

// assert results from `Foo->someMethod()`

因此 Foo->someMethod() 将根据从 Bar 对象接收到的结果生成数据。但是,每当数组长于 1 时,就会出现以下错误:

There was 1 failure:

1) testMyTest(FooTest) with data set #2 (array(0.5, 0.5))
Expectation failed for method name is equal to <string:GetValue> when invoked zero or more times.
Mocked method does not exist.
/usr/share/php/PHPUnit/Framework/MockObject/Mock.php(193) : eval()'d code:25

我的一个想法是使用 ->will($this->returnCallback()) 来调用回调方法,但是我不知道如何向回调指示哪个 Bar 对象正在进行调用(以及因此给出什么响应)。

另一个想法是使用 onConsecutiveCalls() 方法或类似的方法来告诉我的存根第一次返回 1,第二次返回 2,等等,但我不确定具体如何做这个。我还担心,如果我的类除了对集合进行有序迭代之外执行任何其他操作,我将无法对其进行测试。

I'm building unit tests for class Foo, and I'm fairly new to unit testing.

A key component of my class is an instance of BarCollection which contains a number of Bar objects. One method in Foo iterates through the collection and calls a couple methods on each Bar object in the collection. I want to use stub objects to generate a series of responses for my test class. How do I make the Bar stub class return different values as I iterate? I'm trying to do something along these lines:

$stubs = array();
foreach ($array as $value) {
    $barStub = $this->getMock('Bar');
    $barStub->expects($this->any())
            ->method('GetValue')
            ->will($this->returnValue($value));
    $stubs[] = $barStub;
}
// populate stubs into `Foo`

// assert results from `Foo->someMethod()`

So Foo->someMethod() will produce data based on the results it receives from the Bar objects. But this gives me the following error whenever the array is longer than one:

There was 1 failure:

1) testMyTest(FooTest) with data set #2 (array(0.5, 0.5))
Expectation failed for method name is equal to <string:GetValue> when invoked zero or more times.
Mocked method does not exist.
/usr/share/php/PHPUnit/Framework/MockObject/Mock.php(193) : eval()'d code:25

One thought I had was to use ->will($this->returnCallback()) to invoke a callback method, but I don't know how to indicate to the callback which Bar object is making the call (and consequently what response to give).

Another idea is to use the onConsecutiveCalls() method, or something like it, to tell my stub to return 1 the first time, 2 the second time, etc, but I'm not sure exactly how to do this. I'm also concerned that if my class ever does anything other than ordered iteration on the collection, I won't have a way to test it.

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

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

发布评论

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

评论(3

心的位置 2024-09-02 07:45:51

不幸的是,我不确定您是否可以使用 getMock() 解决您的实际问题,但我对 getMock() 本身的经验很少。

我唯一能立即想到的事情,但不知道你的 Bar 类,这可能没有帮助: getMock() 的第三个参数允许你传递构造函数参数(作为数组) )。

我将创建我自己的模拟类,扩展 Bar 作为测试助手(“只是另一个类,恰好只在测试中使用”的奇特名称),它完全按照我喜欢的方式进行操作,并将一系列的注入到您的 中Foo 对象。这为您提供了所需的所有控制,因为您可以直接替换有问题的方法,而 getMock() 则不能这样做。当然,这也意味着您在此测试中没有测试 Bar 类,这可能不是您想要的 - 尽管我建议无论如何为每个测试类编写一个单独的测试类,但是有在这种情况下,这是不必要的纯粹。

$stubs = array();
foreach ($array as $value) {
    $stubs[] = new MyBarTestHelper($value);
}

除此之外,我真的很惊讶你只看到当你有多个数组元素时描述的异常。我观察到 PHPUnit 实际上希望您声明任何您希望它能够作为 getMock() 参数进行跟踪的方法,否则会错误地输出,因为本质上它内部所做的是创建它自己的类扩展,用逻辑包装您明确声明的每个方法,使其确定是否被调用(=将方法名称添加到逻辑列表中)。

所以,我很天真(说真的,我可能是,我自己是一个测试新手),但看看这是否对你有帮助:

$stubs = array();
foreach ($array as $value) {
    $barStub = $this->getMock('Bar', array('GetValue'));
    $barStub->expects($this->any())
            ->method('GetValue')
            ->will($this->returnValue($value));
    $stubs[] = $barStub;
}

I'm unfortunately not sure if you can solve your actual question using getMock(), but my experience with getMock() itself is slim.

Only thing I can think of offhand, but not knowing your Bar class, this may not help: The third parameter of getMock() lets you pass constructor arguments (as an array).

I'd create my own mock class extending Bar as a test helper (fancy name for 'just another class that so happens to be used only in tests') that does exactly what I like and inject a series of them into your Foo object. That gives you all the control you'd want, since you can outright replace the methods in question, which getMock() does not do. Of course that also means you're not testing the Bar class in this test, which may not be what you want - though I'd recommend writing a separate test class per tested class anyway, but there are cases where that's unnecessarily purist.

$stubs = array();
foreach ($array as $value) {
    $stubs[] = new MyBarTestHelper($value);
}

That aside, I'm honestly surprised you're only seeing the exception described when you have more than one array element. I've observed that PHPUnit actually expects you to declare any method you want it to be able to track as a getMock() parameter, and will stolidly error out otherwise, since essentially what it does internally is create its own extension of the class, wrapping each method that you expressly declare with logic that lets it determine whether it was called (= adding the method name into a logical list).

So colour me naive (seriously, I probably am, I'm a test newbie, myself), but see if this helps you any:

$stubs = array();
foreach ($array as $value) {
    $barStub = $this->getMock('Bar', array('GetValue'));
    $barStub->expects($this->any())
            ->method('GetValue')
            ->will($this->returnValue($value));
    $stubs[] = $barStub;
}
梦一生花开无言 2024-09-02 07:45:51

如果您习惯使用 global,这应该满足调用时按顺序返回一系列值的要求。它不知道调用了哪个 Bar,但如果 Foo 按顺序调用每个 Bar 一次,那么填充测试数据应该不会太难。

$barTestData = array('empty',1,2,3,4,5,6);

function barDataCallback(){
    global $barTestData;
    return next($barTestData);
}

This should satisfy the requirement to return a series of values in order as it's called if you're comfortable with the use of global. It has no idea which Bar is called but if each Bar is called by Foo once in order then it shouldn't be too hard to populate the test data.

$barTestData = array('empty',1,2,3,4,5,6);

function barDataCallback(){
    global $barTestData;
    return next($barTestData);
}
生寂 2024-09-02 07:45:51

我注意到您的代码中的“->method('GetValue')”后面有一个额外的括号。不知道你有没有复制粘贴。

I noticed you have an extra parenthesis after "->method('GetValue')" in your code. Don't know if you copied and pasted that or not.

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