简体   繁体   English

PHPUnit:验证数组是否具有具有给定值的键

[英]PHPUnit: Verifying that array has a key with given value

Given the following class:鉴于以下类:

<?php
class Example {
    private $Other;

    public function __construct ($Other)
    {
        $this->Other = $Other;
    }

    public function query ()
    {   
        $params = array(
            'key1' => 'Value 1'
            , 'key2' => 'Value 2'
        );

        $this->Other->post($params);
    }
}

And this testcase:这个测试用例:

<?php
require_once 'Example.php';
require_once 'PHPUnit/Framework.php';

class ExampleTest extends PHPUnit_Framework_TestCase {

    public function test_query_key1_value ()
    {   
        $Mock = $this->getMock('Other', array('post'));

        $Mock->expects($this->once())
              ->method('post')
              ->with(YOUR_IDEA_HERE);

        $Example = new Example($Mock);
        $Example->query();
    }

How do I verify that $params (which is an array) and is passed to $Other->post() contains a key named 'key1' that has a value of 'Value 1'?我如何验证$params (它是一个数组)并传递给$Other->post()包含一个名为“key1”的键,它的值为“Value 1”?

I do not want to verify all of the array - this is just a sample code, in actual code the passed array has a lot more values, I want to verify just a single key/value pair in there.我不想验证所有数组 - 这只是一个示例代码,在实际代码中,传递的数组有更多值,我只想验证其中的单个键/值对。

There is $this->arrayHasKey('keyname') that I can use to verify that the key exists.我可以使用$this->arrayHasKey('keyname')来验证密钥是否存在。

There is also $this->contains('Value 1') , which can be used to verify that the array has this value.还有$this->contains('Value 1') ,可用于验证数组是否具有此值。

I could even combine those two with $this->logicalAnd .我什$this->logicalAnd可以将这两者与$this->logicalAnd结合起来。 But this of course does not give the desired result.但这当然不会给出预期的结果。

So far I have been using returnCallback, capturing the whole $params and then doing asserts on that, but is there perhaps another way to do what I want?到目前为止,我一直在使用 returnCallback,捕获整个 $params 然后对其进行断言,但是也许有另一种方法可以做我想做的事?

The $this->arrayHasKey('keyname'); $this->arrayHasKey('keyname'); method exists but its name is assertArrayHasKey : 方法存在,但其名称为assertArrayHasKey

// In your PHPUnit test method
$hi = array(
    'fr' => 'Bonjour',
    'en' => 'Hello'
);

$this->assertArrayHasKey('en', $hi);    // Succeeds
$this->assertArrayHasKey('de', $hi);    // Fails

In lieu of creating a re-usable constraint class, I was able to assert an array key's value using the existing callback constraint in PHPUnit. 代替创建可重用的约束类,我能够使用PHPUnit中现有的回调约束来声明数组键的值。 In my use case, I needed to check for an array value in the second argument to a mocked method ( MongoCollection::ensureIndex() , if anyone is curious). 在我的用例中,我需要在模拟方法的第二个参数中检查数组值( MongoCollection :: ensureIndex() ,如果有人好奇的话)。 Here's what I came up with: 这是我想出的:

$mockedObject->expects($this->once())
    ->method('mockedMethod')
    ->with($this->anything(), $this->callback(function($o) {
        return isset($o['timeout']) && $o['timeout'] === 10000;
    }));

The callback constraint expects a callable in its constructor, and simply invokes it during evaluation. 回调约束在其构造函数中期望有一个可调用的对象,并且在评估期间仅调用它。 The assertion passes or fails based on whether the callable returns true or false. 根据可调用对象返回的是true还是false,断言是通过还是失败。

For a large project, I'd certainly recommend creating a re-usable constraint (as in the above solution) or petitioning for PR #312 to be merged into PHPUnit, but this did the trick for a one-time need. 对于大型项目,我当然会建议创建一个可重用的约束(如上述解决方案中所述)或请求将PR#312合并到PHPUnit中,但这确实满足了一次性需要。 It's easy to see how the callback constraint might be useful for more complicated assertions, too. 很容易看出,回调约束对于更复杂的断言也可能很有用。

I ended up creating my own constraint class, based on the attribute one 我最终根据属性一创建了自己的约束类

<?php
class Test_Constraint_ArrayHas extends PHPUnit_Framework_Constraint
{
    protected $arrayKey;

    protected $constraint;

    protected $value;

    /**
     * @param PHPUnit_Framework_Constraint $constraint
     * @param string                       $arrayKey
     */
    public function __construct(PHPUnit_Framework_Constraint $constraint, $arrayKey)
    {
        $this->constraint  = $constraint;
        $this->arrayKey    = $arrayKey;
    }


    /**
     * Evaluates the constraint for parameter $other. Returns TRUE if the
     * constraint is met, FALSE otherwise.
     *
     * @param mixed $other Value or object to evaluate.
     * @return bool
     */
    public function evaluate($other)
    {
        if (!array_key_exists($this->arrayKey, $other)) {
            return false;
        }

        $this->value = $other[$this->arrayKey];

        return $this->constraint->evaluate($other[$this->arrayKey]);
    }

    /**
     * @param   mixed   $other The value passed to evaluate() which failed the
     *                         constraint check.
     * @param   string  $description A string with extra description of what was
     *                               going on while the evaluation failed.
     * @param   boolean $not Flag to indicate negation.
     * @throws  PHPUnit_Framework_ExpectationFailedException
     */
    public function fail($other, $description, $not = FALSE)
    {
        parent::fail($other[$this->arrayKey], $description, $not);
    }


    /**
     * Returns a string representation of the constraint.
     *
     * @return string
     */
    public function toString ()
    {
        return 'the value of key "' . $this->arrayKey . '"(' . $this->value . ') ' .  $this->constraint->toString();
    }


    /**
     * Counts the number of constraint elements.
     *
     * @return integer
     */
    public function count ()
    {
        return count($this->constraint) + 1;
    }


    protected function customFailureDescription ($other, $description, $not)
    {
        return sprintf('Failed asserting that %s.', $this->toString());
    }

It can be used like this: 可以这样使用:

 ... ->with(new Test_Constraint_ArrayHas($this->equalTo($value), $key));

In case you wish to do some complex testing on the parameter, and also have useful messages and comparisons, there is always the option of placing assertions within the callback. 如果您希望对参数进行一些复杂的测试,并获得有用的消息和比较结果,则始终可以选择在回调中放置断言。

eg 例如

$clientMock->expects($this->once())->method('post')->with($this->callback(function($input) {
    $this->assertNotEmpty($input['txn_id']);
    unset($input['txn_id']);
    $this->assertEquals($input, array(
        //...
    ));
    return true;
}));

Notice that the callback returns true. 请注意,回调返回true。 Otherwise, it would always fail. 否则,它将始终失败。

如果您不介意在密钥不存在(而不是失败)时出现错误,那么您可以使用以下方法。

$this->assertEquals('Value 1', $params['key1']);

Sorry, I'm not an English speaker. 抱歉,我不会说英语。

I think that you can test if a key exists in the array with the array_key_exists function, and you can test if the value exists with array_search 我认为您可以使用array_key_exists函数测试数组中是否存在键,并且可以使用array_search测试值是否存在

For example: 例如:

function checkKeyAndValueExists($key,$value,$arr){
    return array_key_exists($key, $arr) && array_search($value,$arr)!==false;
}

Use !== because array_search return the key of that value if exists and it may be 0. 使用!==因为array_search返回该值的键(如果存在),并且可以为0。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM