草庐IT

php - 用具体值模拟 atLeastOnce,其余的不重要

coder 2024-04-06 原文

问题在 PHP 中,但适用于使用 xUnit 框架的任何语言。

我想要一个 mock,需要 140 次调用 jump 方法。
我需要验证,至少一次有一个以 500 作为参数的调用。
我不在乎是否所有调用都是 500,但我至少需要一个调用了 500 的调用。

$mock = $this->getMock('Trampoline', ['jump']);

$mock->expects($this->atLeastOnce())
     ->method('jump')
     ->with($this->equalTo(500))
     ->will($this->returnValue(true));

$sportsman->setTramploine($mock);
$sportsman->jumpToRandomHeights($times = 140); // this calls Trampoline->jump
// I need to verify the sportsman had jumped 
// to the height of 500 at least once out of the 140 jumps he is performing

在当前代码中,测试在第一次调用 jump 后失败,因为第一次调用的值不同于 500,这意味着 atLestOnce 这里只是表示应该调用该方法,而不是在其他调用中以特定值调用它。


解决方案

缺少的信息是在 with 中使用回调。感谢 edorian 在下面的回答,结果是这样的:

$testPassed = false;

$checkMinHeight = function ($arg) use(&$testPassed)
{
    if($arg === 500)
        $testPassed = true;

    // return true for the mock object to consider the input valid
    return true;
}


$mock = $this->getMock('Trampoline', ['jump'])
    ->expects($this->atLeastOnce())
    ->method('jump')
    ->with($checkMinHeight)
    ->will($this->returnValue(true));

$sportsman->setTramploine($mock);
$sportsman->jumpToRandomHeights($times = 1000); // this calls Trampoline->jump
// I need to verify the sportsman had jumped 
// to the height of 500 at least once out of the 1000 jumps he is performing


$this->assertTrue($testPassed, "Sportsman was expected to 
    jump 500m at least once");

最佳答案

你可以,但我能想到的使用 PHPUnits 模拟 API 的最佳实现看起来仍然很令人毛骨悚然。

另一种更易读的解决方法是创建您自己的 Trampoline 子类并在那里实现它。

但对于挑战:

假设这个类:

<?php
class FancyMocking {
    function doThing($value) { }
}

并且我们有 $x 调用,其中一个必须有 $value > 200:


<?php

class FancyMockingTest extends PHPUnit_Framework_TestCase {

    public function testAtLeastOfMy200CallsShouldHaveAValueGreaterThan500() {
      $maxInvocations = 200;

      $mock = $this->getMock('FancyMocking');
      $mock->expects($this->exactly($maxInvocations))
        ->method('doThing')
        ->with($this->callback(function ($value) use ($maxInvocations) { 
            static $invocationCount = 0;
            static $maxValue = 0;

            $maxValue = max($value, $maxValue);
            /* The assertion function will be called twice by PHPUnit due to implementation details, so the *2 is a hack for now */
            if (++$invocationCount == $maxInvocations * 2) { 
                $this->assertGreaterThan(200, $maxValue, 'in 500 tries the max value didn\'t to over 200');
            } 
            return true;
        }))
        ->will($this->returnCallback(function ($value) { 
            return $value >= 200;
        }));
     for($i = $maxInvocations - 2; $i; --$i) { 
          $mock->doThing(50);
     } 
     var_dump($mock->doThing(250));
     var_dump($mock->doThing(50));
    }


}

这将产生:

PHPUnit 3.7.9 by Sebastian Bergmann.

.bool(true)
bool(false)


Time: 0 seconds, Memory: 2.75Mb

OK (1 test, 2 assertions)

意味着 250 的调用返回 true 并且整个测试用例有效。

如果失败:

为了让它失败,我们将 var_dump($mock->doThing(250)); 更改为 var_dump($mock->doThing(70)); 并运行再次:

PHPUnit 3.7.9 by Sebastian Bergmann.

Fbool(false)


Time: 0 seconds, Memory: 2.75Mb

There was 1 failure:

1) FancyMockingTest::testAtLeastOfMy200CallsShouldHaveAValueGreaterThan500
Expectation failed for method name is equal to <string:doThing> when invoked 200 time(s)
in 500 tries the max value didn't to over 200
Failed asserting that 70 is greater than 200.

.../FancyMockingTest.php:29

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

不使用 PHPUnits 模拟 API 的解决方案类似于

class FancyMockingFakeImplementation extends FancyMocking,使用它代替模拟并在那里编写自定义逻辑。

关于php - 用具体值模拟 atLeastOnce,其余的不重要,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13550914/

有关php - 用具体值模拟 atLeastOnce,其余的不重要的更多相关文章

  1. ruby - 如何模拟 Net::HTTP::Post? - 2

    是的,我知道最好使用webmock,但我想知道如何在RSpec中模拟此方法:defmethod_to_testurl=URI.parseurireq=Net::HTTP::Post.newurl.pathres=Net::HTTP.start(url.host,url.port)do|http|http.requestreq,foo:1endresend这是RSpec:let(:uri){'http://example.com'}specify'HTTPcall'dohttp=mock:httpNet::HTTP.stub!(:start).and_yieldhttphttp.shou

  2. ruby-on-rails - 在这种情况下我如何模拟一个对象?没有明显的方法可以用模拟替换对象 - 2

    假设我在Store的模型中有这个非常简单的方法:defgeocode_addressloc=Store.geocode(address)self.lat=loc.latself.lng=loc.lngend如果我想编写一些不受地理编码服务影响的测试脚本,这些脚本可能已关闭、有限制或取决于我的互联网连接,我该如何模拟地理编码服务?如果我可以将地理编码对象传递到该方法中,那将很容易,但我不知道在这种情况下该怎么做。谢谢!特里斯坦 最佳答案 使用内置模拟和stub的rspecs,你可以做这样的事情:setupdo@subject=MyCl

  3. ruby - "public/protected/private"方法是如何实现的,我该如何模拟它? - 2

    在ruby中,你可以这样做:classThingpublicdeff1puts"f1"endprivatedeff2puts"f2"endpublicdeff3puts"f3"endprivatedeff4puts"f4"endend现在f1和f3是公共(public)的,f2和f4是私有(private)的。内部发生了什么,允许您调用一个类方法,然后更改方法定义?我怎样才能实现相同的功能(表面上是创建我自己的java之类的注释)例如...classThingfundeff1puts"hey"endnotfundeff2puts"hey"endendfun和notfun将更改以下函数定

  4. ruby - 在 RSpec 中 stub /模拟全局常量 - 2

    我有一个gem,它有一个根据Rails.env的不同行为的方法:defself.envifdefined?(Rails)Rails.envelsif...现在我想编写一个规范来测试这个代码路径。目前我是这样做的:Kernel.const_set(:Rails,nil)Rails.should_receive(:env).and_return('production')...没关系,只是感觉很丑。另一种方法是在spec_helper中声明:moduleRails;end而且效果也很好。但也许有更好的方法?理想情况下,这应该有效:rails=double('Rails')rails.sho

  5. ruby-on-rails - rspec 模拟对象属性赋值 - 2

    我有一个rspec模拟对象,一个值赋给了属性。我正在努力在我的rspec测试中满足这种期望。只是想知道语法是什么?代码:defcreate@new_campaign=AdCampaign.new(params[:new_campaign])@new_campaign.creationDate="#{Time.now.year}/#{Time.now.mon}/#{Time.now.day}"if@new_campaign.saveflash[:status]="Success"elseflash[:status]="Failed"endend测试it"shouldabletocreat

  6. ruby - 如何使用 rspec stub /模拟对命令行的调用? - 2

    我正在尝试测试命令行工具的输出。如何使用rspec来“伪造”命令行调用?执行以下操作不起作用:it"shouldcallthecommandlineandreturn'text'"do@p=Pig.new@p.should_receive(:run).with('my_command_line_tool_call').and_return('resulttext')end如何创建stub? 最佳答案 使用newmessageexpectationsyntax:规范/虚拟规范.rbrequire"dummy"describeDummy

  7. ruby-on-rails - 这个 C 和 PHP 程序员如何学习 Ruby 和 Rails? - 2

    按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭9年前。我来自C、php和bash背景,很容易学习,因为它们都有相同的C结构,我可以将其与我已经知道的联系起来。然后2年前我学了Python并且学得很好,Python对我来说比Ruby更容易学。然后从去年开始,我一直在尝试学习Ruby,然后是Rails,我承认,直到现在我还是学不会,讽刺的是那些打着简单易学的烙印,但是对于我这样一个老练的程序员来说,我只是无法将它

  8. ruby - Camping 和 Sinatra 之间有什么重要区别吗? - 2

    我的感觉是Camping和Sinatra之间的差异不是很大,您可以安全地选择其中任何一个并且没问题。但我想问问Ruby专家,这是不是真的。Sinatra和Camping微框架之间实际上有什么重要区别吗?您将如何决定使用哪一个? 最佳答案 我知道的唯一显着区别是Camping像Rails一样基于MVC模式,并且与ActiveRecord耦合。Sinatra更加不可知。Camping也不再维护,而Sinatra正在积极开发中。仅这一点就足以让我们先看看Sinatra。编辑:感谢Philippe的更正,很高兴听到Camping的开发正在进

  9. ruby - 接收 block 作为参数的模拟方法 - 2

    我有一个或多或少这样的场景classAdefinitialize(&block)b=B.new(&block)endend我正在对A类进行单元测试,我想知道B#new是否正在接收传递给A#new的block。我使用Mocha作为模拟框架。这可能吗? 最佳答案 我用Mocha和RSpec都试过了,虽然我可以通过测试,但行为不正确。从我的实验中,我得出结论,验证block是否已通过是不可能的。问题:为什么要传递一个block作为参数?block将用于什么目的?什么时候调用?也许这确实是您应该用类似的东西测试的行为:classBlockP

  10. ruby - =~ 运算符的顺序重要吗? - 2

    下面两个语句除了编码风格有区别吗?/regex/=~"some_string_with_regex""some_string_with_regex"=~/regex/ 最佳答案 是的,有区别。正如在http://www.ruby-doc.org/core/classes/Regexp.html#M001232中提到的If=~isusedwitharegexpliteralwithnamedcaptures,capturedstrings(ornil)isassignedtolocalvariablesnamedbythecaptur

随机推荐