Question

I seem to be having some trouble testing that a private instance variable was set. My idea was to stub out the class and make the instance variable public so I could test the setter method. This seems simple enough, but I can't seem to get it to work correctly. Or maybe there's some better way to test setting a private variable.

Class

<?php
namespace PureChat\src\messages;

/**
 * Message class for containing information about a message.
 */
class Message
{
    /**
     * Contains the message text.
     * @var string
     */
    private $messageText;

    /**
     * Sets the instance variable $messageText.
     * @param string $text The text to assign.
     */
    public function setText($text)
    {
        $this->messageText = $text;
    }
}

PHPUnit Test

<?php
use PureChat\src\messages\Message;

class MessageTest extends \PHPUnit_Framework_TestCase
{
    public function testMessageCanSetText()
    {
        $message = new MessageStub;
        $message->setText('test-text');
        $this->assertEquals(
            $message->messageText, 'test-text');
    }
}

class MessageStub extends Message
{
    public $messageText;
}

When run, the test fails with "Failed asserting that 'test-text' matches expected null." My first thought was that maybe the stub class wasn't inheriting the setText method, so I tested that as well with method_exists, however the method does exist, so I'm at a loss.

Was it helpful?

Solution

Private properties are not inherited, so $messageText already is not inside the MessageStub class. And it cannot be set by the inherited method setText.

And then you are using assertEquals() arguments in the wrong order: The first one should be the value you expect, and the second one is the value you test. Flip the arguments - then the error message makes more sense, because currently the message says you expect NULL - but you expect the string 'test-text'.

And then we come to the topic of testing philosophy. A good unit test only checks the outside of an object, but should not care about the inner workings. If you set a value, the success of setting it should somehow be detectable from the outside. When you only have the very basic setter/getter combo, it is indeed very boring to test them, but that's what you should do: set a valid value, get it back, assert it is the same as before, and assert that no errors occurred (this is done automatically by PHPUnit because any PHP error or exception would make the test fail).

If there is no getter for that value - either there is no use in setting the value anyways because it is never used, or you can test it by testing the part that uses the value.

$message->setText('foo');
$message->saveText(); // uses value from setText()

If you want to test this, you'd probably call these two methods in one test. And you need to test that calling saveText alone will trigger an error. Or saves the default value.

Or you come to the conclusion that having TWO methods to do one thing is not a very good idea because testing them is not that easy, so your experience from testing may make you think about improving the API of that message object. You probably want to get rid of that setText method somehow.

Yes, using Reflection to get access to the private property is a way to test it. But now you are tightly binding your test to the internal construction of the object. If you rename the private property, your test breaks - but it shouldn't break because you haven't change the public interface.

OTHER TIPS

You could use reflection.

$message = new Message;
$message->setText('test-text');

$property = (new \ReflectionObject($message))->getProperty('messageText');
$property->setAccessible(true);
$value = $property->getValue($message);
$property->setAccessible(false); // restore state

echo $value;
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top