2

While unit testing in PHPUnit, I'm in a situation where I need to check if an array contains al least one object of a specific type.

Here's a trivial example of what I'm looking for

$obj_1 = new Type1;
$obj_2 = new Type2; 

$container = array( $obj_1, $obj_2 );

// some logic and array manipulation here

// need something like this
$this->assertArrayHasObjectOfClass( 'Type1', $container );

Obviously I can do that with custom code, but is there any assertion (or combination of them) which allows me to do that?

I need to do that many times in multiple tests so, if the assertion I need doesn't esist, how do I extend the set of PHPUnit assertions?

EDIT: custom solution with trait

As suggested by Vail, I came up with a custom solution for this using traits. Here's a semplified version.

// trait code
trait CustomAssertTrait
{
    public function assertArrayHasObjectOfType( $type, $array, $message = '' ) {

        $found = false;

        foreach( $array as $obj ) {
            if( get_class( $obj ) === $type ) {
                $found = true;
                break;
            }
        }

        $this->assertTrue( $found, $message );

    }
}

// test code
class CustomTest extends PHPUnit_Framework_TestCase {
   use CustomAssertTrait;

   // test methods... 
}
4
  • It would be nice if you could provide a minimal working example of what you want to achieve. Commented Apr 2, 2015 at 9:55
  • If found you probably want to break out of the loop. Commented Apr 2, 2015 at 22:49
  • You can try assertInstanceOf() as the example. Commented Apr 7, 2015 at 19:49
  • I don't think I can because I have to put it in a for/foreach loop and if the first occurrence is not the class I'm looking for, then the execution will stop. Commented Apr 10, 2015 at 13:40

4 Answers 4

6

If you check only one object use method assertContains() and place array or Traversable as second parameter. But here object must have same properties as one you are looking for (eg. id, title, etc.) ex.

$obj_x = new Type1;
$this->assertContains($obj_x, $container);

If all objects in array or Traversable have same ancestor or interface, you can use method assertContainsOnlyInstancesOf() ex.

$this->assertContainsOnlyInstancesOf(Type1Type2Interface::class, $container);

https://phpunit.de/manual/5.7/en/appendixes.assertions.html#appendixes.assertions.assertContainsOnlyInstancesOf

Sign up to request clarification or add additional context in comments.

Comments

1

Answering since I was looking for an info too and found that while digging in methods list:

  • phpunit-version: 9.5.13
  • method name: assertContainsOnly
  • example: $this->assertContainsOnly(Dto::class, $this->provider->provide());

Comments

0

I don`t think that there is an assertion for this. You will need to write some custom code.

About extendig PHPUnit assertions you could create trait with your own assertion method and add it to specific tests.

Or look at this https://phpunit.de/manual/current/en/extending-phpunit.html

Comments

0

Similar to your solution:

$found = (bool)array_filter($array, function($v) use ($type) {
                                        return get_class($v) === $type;
                                    });

3 Comments

This is a smart solution to have everything in one line. But I think that my solution with your suggestion (I added a break in foreach loop) it's a bit memory safer. Moreover, I don't know how PHP closures are implemented on low level in case of array_filter. I mean, is the closure created everytime it loops or is it created once and then used by all the iterations?
Created once. array_filter is small wrapper around efficient C code. It's faster or as fast as any php loop without early exit.
the array is the first arg and not the callback

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.