Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

PHPUnit and Abstract classes: how to test concrete constructor that accepts parameters and other concrete methods

I have a very simple class like this one below:

abstract class Person
{
    private $id;
    private $createdOn;

    // ... More private properties

    protected $unfound = array();

The constructor does a foreach on the passed array $data and, using the correct methods, assigns values to properties. If the method doesn't exist, then the key is added to a protected array to keep a trace of it (i called it $unfound, just to be original!).

    public function __construct($data)
    {
        foreach ($data as $field => $value)
        {
            $method = 'set' . ucfirst($field);

            if (method_exists($this, $method))
            {
                $this->$method($value);
            }
            else
            {
                $this->unfound[] = $field;
            }
        }
    }

The list of methods to set the values for properties

    public function setId($id) {
        $this->id = $id;
    }

    public function setCreatedOn($createdOn) {
        $this->createdOn = $createdOn;
    }

And a list of methods to get those assigned values

    public function getId() {
        return $this->id;
    }

    public function getCreatedOn() {
        return $this->createdOn;
    }
} // END of the class

As you can see, the class doesn't do any complicated task: it accepts an array like

array(
    'id' => 4,
    'createdOn' => '2015-01-07 20:50:00',
    'unknownVar' => 'mah'
    // ... Other properties to set
    );

So the class cycles through the array and use the key to call the correct method to set the value. Nothing complex i think.

More complex is, instead, testing it.

As it is an abstract class, i cannot instantiate it directly, but i have to mock it.

My problem is that i can't pass to constructor the correct parameters to test if the values assignment is correctly done.

I've tried to use something like:

public function testPerson()
{
   $abstractClass = '\My\Namespace\Person';

    $testData = array(
        'id' => 1,
        'createdOn' => '2015-01-07 19:52:00',
        'unfound' => 'Inexistent method'
        );

   $methods = array(
      'getId',
      'setId'
      );

    $mock = $this->getMockBuilder($abstractClass)
        ->setConstructorArgs(array($testData))
        ->setMethods($methods)
        ->getMockForAbstractClass();

    $this->assertEquals($testData['id'], $mock->getId());
}

In testPerson(), the $methods variable doesn't contain all methods i need, but for the testing of the test (please, excuse me for the play with words! :) ) i think they are sufficient.

But PHPUnit tells me that:

Failed asserting that null matches expected 1.

It seems like the constructor isn't called, also if the code coverage tells me that methods are called.

is there anyone who can help me understand what is happening and how can i test this class?

Thank you!

like image 228
Aerendir Avatar asked Jan 07 '15 20:01

Aerendir


1 Answers

The solution is very simple: deleting the $methods variable and the calling to setMethods($methods) solved the problem!

Calling setMethods(), in fact, "stubs" those methods that, without setting a proper fixed value, are set to null (the value I received from the tests result).

The method I tested was stubbed whereas the others not.

So a print_r($mock) revealed that other values were correctly set.

So simple but so hard to find! Thank you anyway to you to make me think, so I solved the problem and asked the question!

like image 117
Aerendir Avatar answered Oct 30 '22 20:10

Aerendir