I've been reading here a few questions regarding the use of unit testing to test private methods and properties. I'm new to unit testing and would like input on the method I'm trying so that my testing can access private/protected properties and methods.
In the test I was working on I wanted to confirm that passing a particular parameter to the object resulted in a property being set. I'm using SimpleTest for my unit testing education and my test method is as follows:
function test__Construction_Should_Properly_Set_Tables() {
$cv = new CVObject( array( 'tables' => $this->standardTableDef ) );
$tables = $cv->tables;
$this->assertEqual( $tables, $this->standardTableDef );
}
Then I wrote a __get method in CVObject as follows:
function __get( $name ) {
$trace = debug_backtrace();
$caller = $trace[1];
$inTesting = preg_match( '/simpletest/', $caller['file'] );
if ( $inTesting ) {
return $this->$name;
} else {
trigger_error( 'Cannot access protected property CVObject::$' .
$name . ' in ' . $trace[0]['file'] . ' on line ' .
$trace[0]['line'],
E_USER_NOTICE );
}
}
My idea in this is that if the calling file is from SimpleTest, go ahead and make the property available for the testing purposes, but if not, trigger the error. This allows me to keep the property private but be able to use it in testing, which is going to be more important to me with a particular private method I'm about to begin writing.
So, my question is, am I missing something really bad here and should avoid this technique?
If you find yourself stuck and simply must access a private/protected property to enable thorough testing, at least place the code that enables access in your test or testing framework. Embedding testing-only code in production code a) complicates the design, b) adds more code that must be tested, and c) means the code runs differently in production.
You can use Ken's subclass method for protected properties, but if you need to access private and are on PHP 5.3.2+ you can use reflection.
function test__Construction_Should_Properly_Set_Tables() {
$cv = new CVObject( array( 'tables' => $this->standardTableDef ) );
$tables = self::getPrivate($cv, 'tables');
$this->assertEqual( $tables, $this->standardTableDef );
}
static function getPrivate($object, $property) {
$reflector = new ReflectionProperty(get_class($object), $property);
$reflector->setAccessible(true);
return $reflector->getValue($object);
}
Note that getPrivate() won't work as written for properties inherited from superclasses, but it's not too hard to loop up the hierarchy to find the declaring class.
While testing a component, you have to test only it's interface (input, output, exceptions), without considering or even knowing it's internal implementation (even better if one programmer writes test cases and the other does the implementation, please refer to XP and TDD techniques). So, the only thing you have to test are public methods.
To ensure, that your private (helper) methods are written correctly, simply use code coverage analyzer (please checkout Code Coverage tools for PHP) and cover as much code as possible with your test cases.
Your solution will guarantee you a maintenance nightmare. Test cases and component implementation should not be coupled in any way, because coupling would need to be bulletproof or otherwise you'll have to test it too.
A quick and dirty solution is to use protected (instead of private) methods, and then test using a wrapper that makes the methods under test public.
class Foo
{
protected function bar(){} // should really be private but protected will do
}
class FooTestWrapper extends Foo
{
public function bar{} { return parent::bar(); } // this is testable
}
But as ljank points out, testing private methods/implementation can become a maintenance nightmare - it probably means you are doing work that should be farmed out to other classes.
Generally speaking, your code should not contain features just for testing. And while it is debatable if testing private/protected methods is good practice, I find that sometimes and want to test a certain private/protected method in isolation.
Using Netsilik/BaseTestCase (MIT License) you can set/get private variable and call private or protected functions:
composer require netsilik/base-test-case
Class to test:
<?php
namespace App;
class Foo
{
private $bar;
protected function setBar(string $bar)
{
$this->bar = $bar;
}
}
Unit test:
class MyTestCase extends \Netsilik\Testing\BaseTestCase
{
public function test_whenProtectedMethodCalled_thenPrivateMemberSet()
{
$foo = new Foo();
self::callInaccessibleMethod($foo, 'setBar', 'abc');
self::assertEquals('abc', self::getInaccessibleProperty($foo, 'bar'));
}
}
Hope this helps.
Related
I have both a "way of doing this" and "how is it done" question(s).
Class realClass {
private $privateProperty;
public function __construct($someArray, $someString) {
// stuff
}
public function setProperty($someArray) {
$this->privateProperty = $someArray;
}
}
class testRealClass extends TestCase {
// creating a mock because don't want to have tests depending on arguments passed to the constructor
public function setUp(): void
{
$this->classToTest = $this->getMockBuilder(realClass::class)
->disableOriginalConstructor()
->setMethods(null)
->getMock();
}
// testing the set of the variable. In order to know if it was set, without using a getter,
// we probably need to use reflection
public function testSetPrivateProperty() {
$this->classToTest->setProperty([1,2,3]);
// This will not work as the property is private
// echo $this->classToTest->privateProperty;
// Reflecting the mock, makes sense? How does this work?
$reflect = new \ReflectionClass($this->classToTest);
$property = $reflect->getProperty('privateProperty');
$property->setAccessible(true);
var_dump($property->getValue($this->classToTest));
}
}
When executing this I get a ReflectionException: Property privateProperty does not exist.
If I do a print_r of the reflections->getProperties I can see the property there.
I understand this would be easily achievable with just a getter in the realClass but take this as an exercise.
Doest this make sense at all or this will not work because it is just wrong?
Thank you everyone
(Wrote the code here so if I'm missing ";", "(", etc, do ignore it :)
Using Reflection API for testing is seldom a good idea. However, the real question is - why have a private property if nothing can access it? Obviously, the property is used in another method of the class. So, the clean solution here is remove all reflection code, then create a getter in the class itself, and replace all calls to $this->property to $this->getProperty(), then you can easily test this.
For a more generalized take on this question, private properties and methods are not meant to be tested, they are private because they conceal their data from everyone, including the tester classes. The more private fields and properties your class has, the less testable it gets, so when this becomes a hindrance, it is better to split the class into two, making one of the private methods public.
Given I have a FruitSalad class (the system under test):
class FruitSalad
{
protected $fruits = [];
public function addFruit(Fruit $fruit)
{
$this->fruits[] = $fruit;
return $this;
}
}
And I have a Fruit class:
class Fruit
{
public static function withName($name)
{
$instance = new MyDependencyClass();
$instance->name = $name;
return $instance;
}
}
A trivial example, however you can see that the Fruit class uses a named static constructor, and the addFruit() method on the FruitSalad class type hints Fruit as its expected parameter.
When writing a test for addFruit(), I need to mock the Fruit class.
function test_it_can_add_a_fruit_to_its_list_of_fruits()
{
$fruit = $this->getMockBuilder('Fruit')
->disableOriginalConstructor()
->getMock();
$this->fruitSalad->addFruit($fruit);
// Do some assertion.
}
This creates a simple mock of the Fruit class, but I want to instantiate it via the withName() static method - and I do not want to expose a setter for the name property.
How can I create a mock for Fruit using the static named constructor?
PHPUnit used to support mocking static methods, but since PHPUnit 4.0 it's omitted. I see four options here:
1. Don't mock the method at all
You could just call the method and use it's logic, although you'd test the static method as well if you do and normally that's something you should avoid when writing unit tests.
2. Change the class
Ask yourself if this method really needs to be static and if not, change the class to test it properly. There are quite some use cases where it's better to change some of your architecture in order to write proper tests.
3. Use a spy class
Spy classes are classes that extend a class that you would usually mock, but implement some logic for testing the configuration of a class or the dependency of a tested method to another method. In the very most cases this can be avoided by mocking the class properly. Spies are simply your work around if mocks are not enough, there are very few cases in which you really need them.
However, in this case a spy could be used to overwrite a static method as a work around:
class FruitSpy extends Fruit
{
public static $return;
public static $name;
public static function withName($name) {
$expected = self::$name;
if($name == $expected) {
return self::$return;
} else {
throw new \RuntimeException("FruitSpy::withName(): Parameter 0 was $name, $expected expected");
}
}
}
This example checks for the correct $name and, if it's correct, returns your defined return. You'd use it like this in your test:
$fruitSpy = new FruitSpy();
$fruitSpy::$name = "Banana";
$fruitSpy::$return = new \stdClass();
$this->fruitSalad->addFruit($fruitSpy);
Not exactly a clean solution, but the only way I see if you absolutely positively don't want to change other code than the test code.
Again, you should think about changing the static method to a casual method if you need to do something like this.
4. Use PHPUni 3.*
You could simple use a deprecated version of PHPUnit to use this method. Not a preferred way either.
Conclusion
I don't see a clean way to mock a static method and ::staticExpects() was removed for a reason in 4.0
How can I create a mock for Fruit using the static named constructor?
You can't. Mocks are created by using a mocking framework.
Anyway it does not matter how mocks are created but, instead, how they behave, because they're external to the class being tested.
Just configure the mock so that it behaves the same way a real Fruit instance would when created using Fruit::withName.
I am confused whether to use static method or just simple method.
Lets me give an example, I am using Zend framework 1 project.
I have class like
class Example1
{
public static function getVariable() {
return is_numeric(Zend_Registry::get('config')->Variable) ? Zend_Registry::get('config')->Variable : 0;
}
public function calculateSome($param1, $param2) {
$response = array();
if($param2 == 0) {
$response = number_format(($param1 * self::getvariable()) /100);
} else {
$response = $param1;
}
return $response;
}
}
Usage :
Currently i'm getting variable value like Example1::getVariable() in whole project.
And Calculating like first instantiating a class $class1 = new Example1(); and then calling the function like $class1->calculateSome(1, 0);
I am confused whether it is good to change calculateSome() to public static and call like this Example1::calculateSome(1, 0) or left as it is.
I Have found link when to use static =>
When to use static vs instantiated classes
But I can't understand what it says.
You can find the long answer here: How Not To Kill Your Testability Using Statics
The TL;DR version of it is:
A static method is nothing more than a namespaced function, Foo::bar() does not significantly differ from foo_bar().
Whenever you call a static method, or a function for that matter, you're hardcoding a dependency. The code that reads $bar = Foo::bar(); has a hardcoded dependency to a specific Foo class. It is not possible to change what Foo refers to without changing that part of the source code.
An object is a "soft" dependency. $bar = $foo->bar(); is flexible, it allows room to change what $foo refers to. You use this with dependency injection to decouple code from other code:
function baz(Foo $foo) {
$bar = $foo->bar();
...
}
You can call Foo::bar() anytime from anywhere. If Foo::bar has some dependency it depends on, it becomes hard to guarantee that this dependency is available at the time you're calling the method. Requiring object instantiation requires the object's constructor to run, which can enforce requirements to be set up which the rest of the methods of the object can depend on.
Constructors together with dependency injecting objects into other functions are very powerful to
create seams in your codebase to make it possible to "take it apart" and put it together in flexible ways
put checks into strategic places to ensure requirements are met for certain parts of the code (at object instantiation time the constructor enforces a sane state of its little part of the world, its object), which makes it a lot easier to localise and contain failures.
Think of it like compartmentalising your app and putting firewalls between each compartment with a supervisor in charge of each one; instead of everyone just running around in the same room.
Any time you write new Class, you may as well write Class::staticMethod(), the hardcoded dependency is the same.
So the decision comes down to:
What are the requirements of this class? Does it need to ensure certain preconditions are met before any of its code can run (e.g. a database connection needs to be available), or are all methods just self-contained little helper methods?
How likely are you to maybe want to substitute this class for another class? Does this class produce side effects (e.g. writing to a file, modifying some global state) which may not always be desirable and hence a different version of it may be useful under some circumstances?
May you need more than one instance of this class at the same time, or is the nature of the class such that there are no individual instances needed?
Start using unit tests, which require you to take your app apart and test each little part individually to ensure it works, and you'll see where the advantage of object instantiation and dependency injection lie.
When the method involve instance-based properties/changes u should keep it non-static.
If its a method that is needed for the whole type then use static.
For example u can keep tracks of created instances by this snippet:
class Product {
static $count;
private $name;
public function __construct($name) {
$this->name = $name;
self::$count++;
}
public function getName() {
return $this->name;
}
public static function getCount() {
return self:$count;
}
}
$productA = new Product('A');
$productB = new Product('B');
echo $productA->getName(). ' and ' . $productB->getName(). '<br />'. PHP_EOL;
echo 'Total made products :' . Product::getCount();
I'm new to unit testing and PHPUnit, but I've reading a lot lately about design patterns and isolated tests and I've decided to refactor an application I'm working on to get rid of static classes, singletons, hardcoded dependencies and anything else defined on the global scope, hopefully making it "testable" and not a pain in the ass to mantain in the future, since it is meant to be a long term project.
So far I believe I understand the theory behind unit testing, but I was wondering, in a scenario where one delegates handling nested dependencies of objects to a Factory, how should one go about unit testing said Factory, or is it just redundant to test it? And what is the best approach to test that the "chain" of dependencies work well in sync?
Let me illustrate the questions. Suppose you have the following "legacy" code:
class House {
protected $material;
protected $door;
protected $knob;
public function __construct() {
$this->door = new Door();
$this->knob = $this->door->getKnob();
$this->material = "stone";
echo "House material: ".$this->material . PHP_EOL . "<br/>";
echo "Door material: ".$this->door->getMaterial() . PHP_EOL . "<br/>";
echo "Knob material: ".$this->knob->getMaterial() . PHP_EOL . "<br/>";
}
}
class Door {
protected $material;
protected $knob;
public function __construct() {
$this->knob = new Knob();
$this->material = "wood";
}
public function getKnob() {
return $this->knob;
}
public function getMaterial () {
return $this->material;
}
}
class Knob {
protected $material;
public function __construct() {
$this->material = "metal";
}
public function getMaterial () {
return $this->material;
}
}
$house = new House();
This is (as far as my understanding goes) bad for unit testing, so we replace the hardcoded dependencies with DI + a Factory class:
class House {
protected $material;
protected $door;
protected $knob;
public function __construct($door) {
$this->door = $door;
$this->knob = $this->door->getKnob();
$this->material = "stone";
echo "House material: ".$this->material . PHP_EOL . "<br/>";
echo "Door material: ".$this->door->getMaterial() . PHP_EOL . "<br/>";
echo "Knob material: ".$this->knob->getMaterial() . PHP_EOL . "<br/>";
}
}
class Door {
protected $material;
protected $knob;
public function __construct($knob) {
$this->knob = $knob;
$this->material = "wood";
}
public function getKnob() {
return $this->knob;
}
public function getMaterial () {
return $this->material;
}
}
class Knob {
protected $material;
public function __construct() {
$this->material = "metal";
}
public function getMaterial () {
return $this->material;
}
}
class HouseFactory {
public function create() {
$knob = new Knob();
$door = new Door($knob);
$house = new House($door);
return $house;
}
}
$houseFactory = new HouseFactory();
$house = $houseFactory->create();
Now (and again, as far as I understand) House, Door and Knob can be unit tested with mocked dependencies just fine. But:
1) What happens with HouseFactory now?
Should one just:
Not test it since it doesn't have any application logic worth testing yet and Factories generally stay that way. Assume that if the independ tests for House, Door & Knob pass the Factory should be fine.
Refactor the factory somehow, ie, using functions within the class to get each instance in such a way that one may override these functions via PHPUnit to return mock objects, just in case there is some extra logic in the class that could use some testing in the future.
2) Is it feasible to set up tests that rely on several (not mocked) dependencies at once? I understand this is technically not unit testing (integration testing perhaps?) but I guess it's still perfectly doable using PHPUnit? Given the example above, I would like to be able to set up a test that not only tests House, Door, Knob and HouseFactory in isolation, but also the results of the interaction of the real objects with each other, perhaps with some of their functions mocked, such as the ones which deal with data. Is PHPUnit a bad choice for this kind of tests?
Thanks in advance for your time. I realize some of the assumptions I'm making may not be correct, since I'm obviously not an expert on the matter; corrections are welcome and appreciated.
The factory is just like the new keyword. Do you test the new keyword? No, you test if you can construct a class. But that's independent to the factory itself and part of the unit so already part of your unit tests.
2) is called integration testing. And you can do that with PHPUnit as well.
Edit - As there was some discussion in comments:
As far as Unit testing is concerned, you could unit-test your factory that it does for what it is for: return a concrete type, a type or any type at all.
There is nothing wrong with that, however it's normally not necessary as constructors of the returned type(s) are already under unit-tests and well that test is really trivial and just data-checking which smells like integration testing. Also those types which have that type from the factory as dependency (and which are under unit-test as well) will make compilation/execution fail if the dependency can not be provided. So everything the factory is for, is already tested, even from both sides. And if the factory does not get consumed, well then you don't need to test it.
I suggest you create once a factory purely TDD style, so to pre-formulate the use and then you'll get a feeling for this. You might want to test other aspects of your factory class(es), but probably this belongs more into integration than unit testing.
And I don't wanted to create the impression that other of your units should actually have hardcoded calls to the factory create method(s) instead of getting the dependency injected. As you should not use new inside your units, you should not use Factory::create therein either. Similar to new, the class-name (Factory) is hard-encoded, not injected. It is a hidden dependency then. But dependencies should not be hidden; but made visible.
You can test it with inheritance.
Just extend House with a FakeHouse for testing, and then check $material, $door and $knob etc. whether they've changed or not after testing.
I have a class that looks something like this:
class Foo {
protected $_arr = array();
public function has($key) {
return array_key_exists($key, $this->_arr);
}
public function add($key, $val) {
$this->_arr[$key] = $val;
}
}
For my PHPUnit tests for these methods, the only way I can think if to test add() is by asserting that has() returns TRUE for the same key after adding it. This makes my testAdd() test dependent on my testHas() test. Conversely, the only way I can think of to test has() is basically doing the exact same steps, but this would make this test dependent on an already dependent test, producing a chicken and egg type problem.
Am I going about this the wrong way? What's a better method for testing stuff like this?
Instead of writing one-test-per-method, design your tests around the functionality the class must provide. You'll have tests that exercise multiple methods, but that's fine because it indicates to the developer that those methods are related. This is where Test Driven Development--where you write the tests while designing the contract for the class before writing the code for the class--really shines.
class FooTest extends PHPUnit_Framework_TestCase
{
function testStartsEmpty() {
$foo = new Foo;
self::assertFalse($foo->has('bar'));
}
function testAddStoresKeys() {
$foo = new Foo;
$foo->add('bar', 'baz');
self::assertTrue($foo->has('bar'));
}
function testAddStoresKeysWithNullValues() {
$foo = new Foo;
$foo->add('bar', null);
self::assertTrue($foo->has('bar'));
}
function testAddStoresValues() {
$foo = new Foo;
$foo->add('bar', 'baz');
self::assertEquals('baz', $foo->get('bar')); // fatal error
}
}
Now that testAddStoresValues() fails, it's time to implement a method to get the value for a given key: Foo::get($key).
PHPUnit allows testing of non-public members.
However, using $sut->has() to find out whether $sut->add() worked is perfectly fine. Also, when you test $sut->add(), you dont need to write a separate test for $sut->has() as well, because it's covered in the $sut->add() test already. Just add a #covers annotation.
In reference to the long dicussions in the comments to #Gordon s answer:
The unit in unit testing is the object not the method! You don't want to test methods in isolation from the other methods of the objects. (If you wanted to do so just use functions ;) ) - It's really important to test how the object behaves and if it does the expected think when called and not how the methods interact internally.
I've recently written a blog post explaining why it is important to not test single methods in isolation:
http://edorian.posterous.com/the-unit-in-unit-testing
Quoting from my post:
Unit testing, in PHP, is about testing the observable behaviors of a class
Most importantly:
Observable from the outside! Nobody cares about the internal state of a class if it never changes the outcome of a method call.
Take this sample:
public function setValue($value) {
$this->value = $value;
}
public function execute() {
if (!$this->value) {
throw new Exception("No Value, no good");
}
return $value * 10; // business logic
}
It sounds trivial but the distinction is important and more easy to overlook when looking at bigger classes.
What do we test there?
IF we don’t setValue AND then call execute an exception is thrown!
IF we DO setValue AND then call execute we get an computed result
So we are testing two behaviors of your class and not the methods in isolation!
IMO you can test behavior of the object. So you can test if has return false before and true after adding some stuff into collection.