Inherited Setter Method Does Not Set Overridden Instance Variable - php

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.

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.

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;

Related

How to unit test a method which does not return anything with phpunit [duplicate]

Suppose I have a class with a private property and associated public getter and setter. I want to test with PHPUnit that the property gets the correct value after the setter has been used or that the getter returns the correct property.
Of course I can test the setter by using the getter to see that the object is storing the correct value, and vice versa for testing the getter. However, this doesn't guarantee that the private property is the one being set.
Say I had the following class. I created a property, getter and setter. But I made a typo in the property name, so the getter and the setter don't actually manipulate the property they're meant to manipulate
class SomeClass
{
private
$mane = NULL; // Was supposed to be $name but got fat-fingered!
public function getName ()
{
return ($this -> name);
}
public function setName ($newName)
{
$this -> name = $newName;
return ($this);
}
}
If I run the following test
public function testSetName ()
{
$this -> object -> setName ('Gerald');
$this -> assertTrue ($this -> object -> getName () == 'Gerald');
}
I would get a pass. However, something very bad has actually happened that I don't want. When setName() is called, it actually creates a new property in the class with the name I thought my private property had, only the one that the setter creates is public! I can demonstrate that with the following code:
$a = new SomeClass;
$a -> setName('gerald');
var_dump ($a -> getName ());
var_dump ($a -> name);
It would output:
string(6) "gerald"
string(6) "gerald"
Is there any way I can access the private properties from PHPUnit so I can write tests that make sure that the properties I think are being get and set actually really are being get and set?
Or is there some other thing I should be doing in a test to catch problems like this without trying to get access to the private state of the object under test?
You can also use Assert::assertAttributeEquals('value', 'propertyName', $object).
See https://github.com/sebastianbergmann/phpunit/blob/3.7/PHPUnit/Framework/Assert.php#L490
For testing properties, I'd make the same arguments I make then talking about testing private methods.
You usually don't want to do this.
It's about testing observable behavior.
If you rename all your properties or decide to store them into an array you should not need to adapt your tests at all. You want your tests to tell you that everything still works! When you need to change the tests to make sure everything still works you lose all the benefits as you also could make an error changing the tests.
So, all in all, you lose the value of you test suite!
Just testing the get/set combinations would be ok enough but usually not every setter should have a getter and just creating them for testing is not a nice thing ether.
Usually, you set some stuff and then tell the method to DO (behavior) something. Testing for that (that the class does what is should do) is the best option for testing and should make testing the properties superfluous.
If you really want to do that there is the setAccessible functionality in PHP reflections API but I can't make up an example where I find this desirable
Finding unused properties to catch bugs / issues like this one:
The PHP Mess Detector As a UnusedPrivateField Rule
class Something
{
private static $FOO = 2; // Unused
private $i = 5; // Unused
private $j = 6;
public function addOne()
{
return $this->j++;
}
}
This will generate two warnings for you because the variables are never accessed
I just want to point out one thing. Let's forget about private fields for a moment and focus on what client of your class cares about. Your class exposes a contract, in this case - ability to alter and retrieve name (via getter and setter). Expected functionality is simple:
when I set name with setName to "Gerald", I expect to get "Gerald" when I call getName
That's all. Client won't (well, shouldn't!) care about internal implementation. Whether you used private field name, hashset or called web service via dynamically generated code - doesn't matter for client. The bug you are currently experiencing, from user point of view - is not a bug at all.
Whether PHPUnit allows you to test private variables - I don't know. But from unit-testing perspective, you shouldn't do that.
Edit (in response to comment):
I understand your concerns about possible exposure of internal state, however I don't think unit testing is the right tool to deal with that. You can come up with a lot of possible scenarios how something might do something else which wasn't planned. Unit tests are by no means cure for all and shouldn't be used as such.
I agree with the others that in general you want to avoid accessing privates in your tests, but for the cases where you need to, you can use reflection to read and write the property.

PHPUnit testing constructorless class

I'm developing unit tests on a project and I came across a class that does not contain constructor.
You may ask yourself, "How does this object exist then?"
Well, in another system, this object is generated and saved in database, so in the system where unit tests are required, there is no constructor defined for that class. But there is a need to test their functions so that if they are modified in the system in the future, the tests will indicate in which situations they are used, this will ensure me not to neglect the other points of the project that use it.
Within this environment, how can you test this type of class?
I tried using mocks, but mock brings all null attributes. In addition, a mocked object only comes as a result of a function that you say previously it will bring. So to test the functions of the object itself, this is not functional.
Any idea?
PS: Sorry for the bad english.
Constructors are optional. A class does not need a constructor for you to be able to instantiate it or test whether its methods are functioning correctly.
As I understand it, you want to test a method which behaves differently depending on a particular property, which would normally be set by a constructor but in your class isn't. That means that in the actual code usage, that property is probably being set directly at some point or there's a different method that sets its value.
In general, for testing these kinds of methods you should always set such a property yourself anyway. The reason for this is simple: a single test should only test one particular method. If you rely on the constructor, your test would be testing the combination of both the constructor and that method. Your test of the method will become dependent on the constructor behaving properly.
Imagine the following:
class Mood {
public $happy;
function __construct() {
$this->happy = true;
}
public function greet() {
if ($this->happy) {
return 'Hi!';
} else {
return 'Go away';
}
}
}
Let's say you want to test the behavior of the greet() method:
class MoodTest extends \PHPUnit\Framework\TestCase {
public function testGreet() {
$mood = new Mood();
$this->assertEqual('Hi!', $mood->greet())
}
}
This test would pass, because we assume the constructor is doing its job and is setting the $happy property to true. But there are two problems here:
We are not testing that the method works properly if $happy is false
We rely on the constructor to set $happy's initial value to true
This means things can change outside our control that would break this specific test, even though the function still works as expected. Business logic may change so that the constructor will set $happy to false initially. Or the developer's logic may change where the constructor disappears entirely and $happy is set in some other way (maybe a setHappy() method is introduced at some point).
To properly test the greet() method, there should be a test for every possible outcome. In this case the method has a single if statement, so there ought to be a test case for both outcomes of that condition:
class MoodTest extends \PHPUnit\Framework\TestCase {
public function testGreetIfHappy() {
$mood = new Mood();
$mood->happy = true;
$this->assertEqual('Hi!', $mood->greet())
}
public function testGreetIfNotHappy() {
$mood = new Mood();
$mood->happy = false;
$this->assertEqual('Go away', $mood->greet())
}
}
Now, no matter what happens to the constructor or business logic, these tests are testing only the behavior of the greet() method. Whatever else the constructor is doing (even if doesn't exist or does nothing at all) no longer has any effect on the tests of the greet() method.

Static property of PHP class to keep value until next usage?

I just stumbled over a PHP class and wonder if there was a valid reason for the way one of it's methods is written.
LogUtility::getLogger() is called as a static method in various other PHP classes of the PHP application. Does the used if statement make sense or is $logManager always null when getLogger() is called?
class LogUtility
{
/**
* #var LogManager
*/
protected static $logManager;
/**
* #return Logger
*/
public static function getLogger($name)
{
if (!self::$logManager) {
self::$logManager = GeneralUtility::makeInstance(LogManager::class);
}
return self::$logManager->getLogger($name);
}
}
You could quickly whip up a test, like below, and test / prove it yourself:
class someClass {
protected static $stored;
public static function test() {
echo '<br>Stored state:' . self::$stored;
if ( ! self::$stored) {
self::$stored = "set";
}
}
}
someClass::test();
someClass::test();
Output is:
Stored state:
Stored state:set
So, based on this simple test, the answer is Yes, the if statement makes sense. The static variable is set and maintained.
$logManager is set to a property of the class, so for now, its pretty much an empty class, just returning with a getter, the instance. This just sets the code to just reuse the object. So a bit of recycling code there.
In the class you are now able to play with this object freely. So if you run this LogUtility from another piece of code by setting it to a var, you have already instantiated it. e.g. $util = new LogUtility();now if someone comes along and tries to instantiate it again, $anotherUtil=new LogUtility(); this recycles the class, passing back the already instantiated instance, instead of instantiating a new one.
Therefore, yes, it kept it. Although, the var doesn't contain the "same value" per say, it contains a reference to the "same" class that was instantiated with he other variable, so it ends up a copy of the same instance there.
It will be null for only the first call in a lifecycle. This implements a design pattern called Singleton.
Check out https://sourcemaking.com/design_patterns/singleton/php/1

Correct way to check if a class has a constant defined with PHPUnit

I am trying to find out the best, or correct, way to check if a class has a constant defined with PHPUnit. The PHPUnit docs don't seem to cover this, which makes me wonder if I am doing the correct thing by testing this - however it is an important feature of my class.
I have the following class:
PurchaseManager.php
/**
* Message sent when a course has been purchased
*/
const COURSE_PURCHASED_MESSAGE = 'coursePurchasedMessage';
...and part of it's test class has this test:
PurchaseManagerTest.php
public function testCoursePurchasedMessageConstant()
{
$pm = new PurchaseManager();
$this->assertTrue(defined(get_class($pm) . '::COURSE_PURCHASED_MESSAGE'));
}
Is this correct? It passes, but i'm just interested to know if this is accurate and best practice.
I am using PHPUnit 5.0.8.
I'm using Reflection class for this purpose. It has getConstants method which returns an associative array [<constant_name> => <constant_value>, ...].
Something like:
public function testHasSiteExportedConstant()
{
$mailer = new \ReflectionClass(SiteExporter::class);
$this->assertArrayHasKey('SITE_EXPORTED', $mailer->getConstants());
}
I would never test for the existence of a constant, attribute, or method. Unless you are testing a code generator, of course.

PHPUnit: Doing assertions on non-public variables

Suppose I have a class with a private property and associated public getter and setter. I want to test with PHPUnit that the property gets the correct value after the setter has been used or that the getter returns the correct property.
Of course I can test the setter by using the getter to see that the object is storing the correct value, and vice versa for testing the getter. However, this doesn't guarantee that the private property is the one being set.
Say I had the following class. I created a property, getter and setter. But I made a typo in the property name, so the getter and the setter don't actually manipulate the property they're meant to manipulate
class SomeClass
{
private
$mane = NULL; // Was supposed to be $name but got fat-fingered!
public function getName ()
{
return ($this -> name);
}
public function setName ($newName)
{
$this -> name = $newName;
return ($this);
}
}
If I run the following test
public function testSetName ()
{
$this -> object -> setName ('Gerald');
$this -> assertTrue ($this -> object -> getName () == 'Gerald');
}
I would get a pass. However, something very bad has actually happened that I don't want. When setName() is called, it actually creates a new property in the class with the name I thought my private property had, only the one that the setter creates is public! I can demonstrate that with the following code:
$a = new SomeClass;
$a -> setName('gerald');
var_dump ($a -> getName ());
var_dump ($a -> name);
It would output:
string(6) "gerald"
string(6) "gerald"
Is there any way I can access the private properties from PHPUnit so I can write tests that make sure that the properties I think are being get and set actually really are being get and set?
Or is there some other thing I should be doing in a test to catch problems like this without trying to get access to the private state of the object under test?
You can also use Assert::assertAttributeEquals('value', 'propertyName', $object).
See https://github.com/sebastianbergmann/phpunit/blob/3.7/PHPUnit/Framework/Assert.php#L490
For testing properties, I'd make the same arguments I make then talking about testing private methods.
You usually don't want to do this.
It's about testing observable behavior.
If you rename all your properties or decide to store them into an array you should not need to adapt your tests at all. You want your tests to tell you that everything still works! When you need to change the tests to make sure everything still works you lose all the benefits as you also could make an error changing the tests.
So, all in all, you lose the value of you test suite!
Just testing the get/set combinations would be ok enough but usually not every setter should have a getter and just creating them for testing is not a nice thing ether.
Usually, you set some stuff and then tell the method to DO (behavior) something. Testing for that (that the class does what is should do) is the best option for testing and should make testing the properties superfluous.
If you really want to do that there is the setAccessible functionality in PHP reflections API but I can't make up an example where I find this desirable
Finding unused properties to catch bugs / issues like this one:
The PHP Mess Detector As a UnusedPrivateField Rule
class Something
{
private static $FOO = 2; // Unused
private $i = 5; // Unused
private $j = 6;
public function addOne()
{
return $this->j++;
}
}
This will generate two warnings for you because the variables are never accessed
I just want to point out one thing. Let's forget about private fields for a moment and focus on what client of your class cares about. Your class exposes a contract, in this case - ability to alter and retrieve name (via getter and setter). Expected functionality is simple:
when I set name with setName to "Gerald", I expect to get "Gerald" when I call getName
That's all. Client won't (well, shouldn't!) care about internal implementation. Whether you used private field name, hashset or called web service via dynamically generated code - doesn't matter for client. The bug you are currently experiencing, from user point of view - is not a bug at all.
Whether PHPUnit allows you to test private variables - I don't know. But from unit-testing perspective, you shouldn't do that.
Edit (in response to comment):
I understand your concerns about possible exposure of internal state, however I don't think unit testing is the right tool to deal with that. You can come up with a lot of possible scenarios how something might do something else which wasn't planned. Unit tests are by no means cure for all and shouldn't be used as such.
I agree with the others that in general you want to avoid accessing privates in your tests, but for the cases where you need to, you can use reflection to read and write the property.

Categories