Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHPUnit: Test array of objects

Just jumped into PHPUnit recently, have been reading stuff about it, trying out some examples, in order to get comfortable in writing the tests for my future projects.

I need to test this scenario, I have Students Class which is like this:

class Students
{
    public function getStudents($studentName, $studentId)
    {       
        $students= array();

        //Instantiating OldStudent Class from Old Project
        $oldStudents = \OldStudents::getStudentByName($studentName, $studentId);

        //Create a Student Object for every OldStudent found on Old Project and set
        //values
        foreach ($oldStudents as $oldStudent)
        {            
            $student = new \Entity\Student();

            //Set Student ID
            $student->setStudentId($oldStudent->getStudentID());

            //Set Student Name
            $student->setStudentName($oldStudent->getStudentName());    
           //.....other setters for student data, irrelevant for this example 

            $students[] = $student;            
        }

        return $students;
    }
}

And the Student Class

Class Student
{
    protected $studentId;
    protected $studentName;

    public function getStudentId()
    {
        return $this->studentId;
    }
    public function setStudentId($studentId)
    {
        $this->studentId = $studentId;
        return $this;
    }
    public function getStudentName()
    {
        return $this->studentName;
    }
    public function setStudentName($studentName)
    {
        $this->studentName = $studentName;
        return $this;
    }   
}

Now how can I test whether the Students Class returns an array of objects with the values set and check the values be using the getters from Student Class

Please do throw some light/information/links whichever directs me in the correct path.

Thanks

like image 777
125369 Avatar asked Jun 24 '13 14:06

125369


People also ask

Which assertion would you use to test if two arrays contain the same elements?

24, $this->assertEquals asserts the array contains the same keys and values, disregarding in what order.

What is PHPUnit testing?

PHPUnit is a unit testing framework for the PHP programming language. It is an instance of the xUnit design for unit testing systems that began with SUnit and became popular with JUnit. Even a small software development project usually takes hours of hard work.

What is assertion in PHPUnit?

Assertions serve to pass or fail the test if a condition is not met. These assertions will look familiar to you if you've used any other testing framework, like PHPUnit. All assertions contain the same common actions attributes: stepKey , before , and after .

What is mock PHPUnit?

Likewise, PHPUnit mock object is a simulated object that performs the behavior of a part of the application that is required in the unit test. The developers control the mock object by defining the pre-computed results on the actions.


2 Answers

I've written some example code below; I guessed the parameters to getStudents were optional filters. We have one test that gets all students. I don't know if they always come back in sorted order, which is why I don't test anything else in the Student class. The second test gets one particular student, and starts to test some of the Student properties.

class StudentsTest extends PHPUnit_Framework_TestCase{

    public function testGetAllStudents(){
        $s=new Students;
        $students=$s->getStudents("","");
        $this->assertIsArray($students);
        $this->assertEquals(7,count($students));
        $first=$students[0];    //Previous assert tells us this is safe
        $this->assertInstanceOf('Student',$first);
    }

    public function testGetOnlyStudentNamedBob(){
        $s=new Students;
        $students=$s->getStudents("Bob","");
        $this->assertIsArray($students);
        $this->assertEquals(1,count($students));
        $first=$students[0];    //Previous assert tells us this is safe
        $this->assertInstanceOf('Student',$first);
        $this->assertEquals('Bob',$first->getStudentName());
    }
}

This is a good first step. After you use it for a while you'll realize it is quite fragile. I.e. you must have exactly 7 students for the first test to pass. There must be exactly one student called Bob for the second to pass. If your \OldStudents::getStudentByName is getting data from a database, it will also be slow; we want unit tests to run as quick as possible.

The fix for both of those is to mock the call to \OldStudents::getStudentByName. Then you can inject your own artificial data, and then you'll only be testing the logic in getAllStudents. Which in turn means that when your unit test breaks there are only 20 or so lines where you could have broken it, not 1000s.

Exactly how to do the mocking is a whole 'nother question, and could depend on PHP version, and how flexible your code setup is. ("OldStudents" sounds like you are dealing with legacy code and maybe you cannot touch it.)

like image 143
Darren Cook Avatar answered Oct 18 '22 21:10

Darren Cook


PHPUnit since version 3.1.4 has the assertion "assertContainsOnly" with the parameter "type" which can assert any PHP type (including instances and internal types), and in at least version 3.7 has the assertion "assertContainsOnlyInstancesOf" which explicitly only checks for class instances, not PHP variable types.

So the test checking if an array contains only objects of a given type is simply:

$this->assertContainsOnlyInstancesOf('Student', $students);

Note that this check implicitly tests that $students is either an array or an object implementing the Traversable interface. Implementing Traversable does not mean you can count, so calling assertCount afterwards to assert a given number of Student objects is present is not guaranteed to succeed, but the added check that the return value in fact is an array seems too much bloat to me here. You are creating and returning an array with something in it - it is safe to assume you can count it. But this might not be the case everywhere.

like image 30
Sven Avatar answered Oct 18 '22 23:10

Sven