Reputation: 3657
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
Upvotes: 19
Views: 62347
Reputation: 28968
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.)
Upvotes: 37
Reputation: 198194
You can do that with assertions. First of all you should aquire an actual result, then do some assertions with it. Compare:
You can assert that it is an array:
class InternalTypeTest extends PHPUnit_Framework_TestCase
{
public function testFailure()
{
$this->assertInternalType('array', 42);
# Phpunit 7.5+: $this->assertIsArray(42);
}
}
Then you could assert that it is not empty (as you know it must return some data):
class NotEmptyTest extends PHPUnit_Framework_TestCase
{
public function testFailure()
{
$this->assertNotEmpty(ARRAY());
}
}
And then you could assert that each value is of your student-type:
class InstanceOfTest extends PHPUnit_Framework_TestCase
{
public function testFailure()
{
$this->assertInstanceOf('Student', new Exception);
}
}
I hope this gives you some pointers. See the link above for a list of common assertions. If you make use of an IDE to wrtie your test it should offer a list of all assertions as well.
Upvotes: 6
Reputation: 70913
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.
Upvotes: 33