Unit test in a PHP class method - Dependency with class attribute? - php

Say I have this class:
class MyExample
{
public $my_var = [1,2,3,4];
public function MyMethod()
{
//Here I use $this->my_var;
}
}
What would be a good way to test the method MyMethod, taking into account that I use the class property $my_var in it. Would you suggest passing the class property as an argument to the method? So I can "give arguments, get a result" approach?:
public function MyMethod($my_var)
{
//Here I use $my_var;
}
I have a feeling that if I don't do that, if I want to test MyMethod, I would have to create several instances of MyExample with different values of $my_var and that would be somehow testing the class itself, and not just the method.
What would be a good approach?

The 'Unit' in 'Unit Test' is best thought of as the class itself. You should be able to refactor the internal implementation of the class extensively, while leaving its public API the same, without breaking your tests. In fact, that's basically what they're for: to allow you to make changes to the implentation of the unit, safe in the knowledge you will be told if you have changed the way it works from the perspective of other classes in your system.

Related

PHPunit: Can I mock class that I test?

I have a class like this:
<?php
class Apple {
public function foo($arg1){
if ($arg1 == 0){
$this->bar($arg1);
}
}
public function bar($arg){
//do something
}
}
And I have a unit test like this:
class AppleTest extends TestCase{
/**
* it's unit test for method Apple::foo
*/
public function testFoo(){
$mock = $this->getMockBuilder('Apple')
->setMethods(['bar'])
->getMock();
$mock->expects($this->once())
->method('bar')
->with($this->equalTo(0));
$mock->foo(0);
}
}
I was told, that I can't use a mock for the class that is being tested. I was told that I should use instance of the class instead of its mock, because when I use mock it's not actual testing. Can some one argue with that.
It isn't that you "can't", but that you really shouldn't. Obviously from your example test, you can mock the object that you are testing.
But this isn't necessarily a good idea. The issue with this is that you are specifying exactly what the code should look like. This can make refactoring more difficult. Suppose that the bar function needs to change and you want to move the functionality the foo needs into a private function. Now your test will fail because foo is no longer calling bar. In this case, it should pass because the functionality hasn't changed. This is one of the great benefits of unit tests, you are able to refactor the code and ensure that things still work correctly.
However, there are times when it is necessary to mock the class that you are testing. Generally this is when you are trying to write new tests for existing code and you aren't able to refactor it to use dependency injection. Then you will have to mock the other function in order to isolate things. This isn't ideal and should be avoided but writing a "bad" test can be better than having no test at all.

How to test a method that uses the cache variable?

<?php
use yii\db\ActiveRecord;
class Model extends ActiveRecord
{
protected static $ids = [];
public static function getIds()
{
if (empty(static::$ids)) {
static::$ids = static::find()->select('id')->column();
}
return static::$ids;
}
}
How to use the test to make sure that the query is executed once by repeatedly calling this method ?
Preferably using codeception or phpunit.
Tests are not just a way to ensure your code works, they also help identify code smells. In your case writing a test is hard, because you use static methods.
There used to be a staticExpects method but that was deprecated in phpunit long ago, so that's not really feasible. The best way to make this code testable is to remove the static keyword. This is easy for getIds() but since the static find() is defined by a 3rd party (yii's ActiveRecord) you can't really remove it. Instead you could wrap it in a non-static method. This gives you the benefit of being able to move away from the Active Record to some other implementation like Doctrine in the future, by just touching these small methods wrapping the 3rd party code.
Once you do this you could create a partial mock of your model to make sure that method is called:
class Model extends ActiveRecord
{
private $ids;
protected function findIds()
{
return static::find()->select('id')->column();
}
public function getIds()
{
if (empty($this->ids)) {
$this->ids = $this->findIds()
}
return $this->ids;
}
}
and in your test:
public function testFindIdsIsCalledWhenGetterIsNotInitialized()
{
$model = $this->getMockBuilder(Model::class)
->setMethods(['findIds'])
->getMock();
$model->expects($this->once())
->method('findIds')
->will($this->returnValue([1, 2, 3]));
$ids = $model->getIds();
$this->assertEquals([1, 2, 3], $ids);
}
This should have 2 assertions, one for the expected method call and one for the returned values. This test bypasses the Active Record and only ensures that your getIds() method works as expected. Another way to approach this is, as mentioned in the comments to your question, to use a functional test that actually tests the database interactions by fetching the data from a (test) database. Obviously since this requires having a database connection and retrieving test data, e.g. from some previously setup fixtures, it's a bit more work and the test will be slower. Depending on how big your project is that might not be an issue and you might feel more comfortable testing the logic in the Active Record implementation as well.

PHPUnit Mocks vs PHP parameter constrains

I'm not sure if it's the proper place for such question since it's rather theoretical than the specific code sample but I'll ask anyway.
So, at some point PHP introduced type constrains in function definition (except basic types of course), i.e.
class A {
public $value;
}
function foo($someInt, A $a) {...}
What make me wondering is if PHPUnit mocks can be used in such situation:
class functionTest extends PHPUnit_Framework_TestCase {
public function testFoo() {
$mockA = $this->getMockBuilder('A')->getMock();
$this->assertEquals('some result', foo(1, $mockA));
}
}
Would such call be accepted when the test runs (ofc. I skipped includes and stuff to keep it simple).
And the more interesting question: if yes, then how is it implemented?
Yes it will be working, PHPUnit will mock your object. This mocked object will dynamically extends the base Object you want to mock.

Using PHPUnit to test helper functions

Let's say I want to test a simple helper that takes a class name as an argument and makes a redirection.
How am I supposed to test this if the function is called in many places from inside a couple of controllers? Should I test every class name that was passed as a parameter in the whole code (write them in the provider function myself)? Or is there a magical function which does that for me?
Your question is the exact reason why dependency injection -- when done correctly (not how most popular frameworks "implement" it) -- is touted as the ultimate in code testability.
To understand why, lets look at how "helper functions" and class-oriented programming make your controllers difficult to test.
class Helpers {
public static function myHelper() {
return 42;
}
}
class MyController {
public function doSomething() {
return Helpers::myHelper() + 100;
}
}
The entire point of unit testing is to verify that "units" of code work in isolation. If you can't isolate functionality, your tests are meaningless because their results could be tainted by the behavior of the other code involved. This can result in what statisticians call Type I and Type II errors: basically, this means you can get test results that might be lying to you.
In the code above, the helper cannot be easily mocked to determine that MyController::doSomething works in complete isolation from outside influences. Why not? Because we can't "mock" the behavior of the helper method to guarantee our doSomething method actually adds 100 to the helper result. We're stuck with the helper's exact behavior (returning 42). This is a problem that correct object-orientation and inversion of control eliminate entirely. Let's consider an example of how:
If MyController asks for it's dependencies instead of using the static helper function , it becomes trivial to mock the outside influences. Consider:
interface AnswerMachine {
public function getAnswer();
}
class UltimateAnswerer implements AnswerMachine {
public function getAnswer() {
return 42;
}
}
class MyController {
private $answerer;
public function __construct(AnswerMachine $answerer) {
$this->answerer = $answerer;
}
public function doSomething() {
return $this->answerer->getAnswer() + 100;
}
}
Now, it's trivially simple to test that MyController::doSomething does in fact add 100 to whatever it gets back from the answer machine:
// test file
class StubAnswerer implements AnswerMachine {
public function getAnswer() {
return 50;
}
}
$stubAnswer = new StubAnswerer();
$testController = new MyController($stubAnswerer);
assert($testController->doSomething() === 150);
This example also demonstrates how the correct use of interfaces in your code can greatly simplify the testing process. Test frameworks like PHPUnit make it very easy to mock interface definitions to perform exactly what you want them to in order to test the isolated functionality of code units.
So I hope these very simple examples demonstrate how powerful dependency injection is when it comes to testing your code. But more importantly, I hope they demonstrate why you should be wary if your framework of choice is using static (just another name for global), singletons, and helper functions.
You cannot test each possible combination of parameters to all the functions you need to test; it will take you longer than the life of the universe. So you use Human Intelligence (some might call it cheating ;-). Test it just once, in this case with a mock controller as the parameter.
Then look at your code and ask yourself if any other object passed in is really going to have it behave differently. For something you describe as a "simple helper" maybe the answer is no. But, if yes, how? Create another mock controller class that simulates that different behaviour. E.g. this second controller might not have the function your helper class expects to call. You expect an exception to be thrown. Create the unit test for that.
Repeat until satisfied.

PHP and Python static methods in objects, two different worlds...?

i'm php coder, trying to get into python world, and it's very hard for me.
Biggest enjoy of static methods in php is automatic builder of instance. No need to declare object, if you needed it once, in every file (or with different constructor params , in one line)
<?php
class Foo {
function __constructor__(){
$this->var = 'blah';
}
public static function aStaticMethod() {
return $this->var;
}
}
echo Foo::aStaticMethod();
?>
we can call constructor from static method don't we? and we can access everything in class as it would be simple method ... we can even have STATIC CONSTRUCTOR in php class and call it like so: Object::construct()->myMethod(); (to pass different params every time)
but not in python???? #staticmethod makes method in class a simple function that doesn't see totally anything ??
class socket(object):
def __init__(self):
self.oclass = otherclass()
print 'test' # does this constructor called at all when calling static method??
#staticmethod
def ping():
return self.oclass.send('PING') # i can't access anything!!!
print Anidb.ping()
I can't access anything from that god damned static method, it's like a standalone function or something like this..??
Maybe I'm using the wrong decorator? Maybe there's something like php offers with static methods in python?
1) Please tell why static methods is isolated
2) Please tell me how to make the same behavior like php static methods have.
3) Please tell me alternative practical use of this, if php static methods behavior is a bad thing
P.s. the goal of all this to write totally less code as much as possible.
P.p.s Heavy commenting of sample code is appreciated
Thank you.
static methods in PHP are not as you believe, they can't access to instance members. No $this! with them.
<?php
class Foo {
public static $var = 'foo ';
function __construct(){
echo 'constructing ';
$this->var = 'blah ';
}
public function aMethod() {
return $this->var;
}
public static function aStaticMethod() {
#return $this->$var; -> you can't do that,
# $this can be accessed only in instance methods, not static
return self::$var;
}
}
$foo = new Foo();
echo $foo->aMethod();
echo Foo::aStaticMethod();
?>
Python has three kind of methods in objects static methods are like functions defined ouside classes, the only use to put them in object is to keep them with the class as helper functions. class methods can access only to variables defined in the class (decorator #classmethod). This is more or less what PHP calls static members or methods. The first parameter of such methods sould be cls, and content of class can be accessed through cls. Normal methods must get self as first parameter and are the only ones to be able to access to instance members.
If you want several objects of the same type you definitely need instances, and the other types are not what you are looking for. If you only have one instance of an object, you could use class methods instead (or PHP static methods).
But in most case you should not bother doing that if you don't know why and just stick with instances of objects and normal methods, doing otherwise is premature optimization and your code is likely to bite you later because of the many restrictions you introduce.
You want classmethod instead. That provides the class as the first argument.
EDIT:
class C(object):
foo = 42
#classmethod
def printfoo(cls):
print cls.foo
C.printfoo()
I see you've already accepted another answer, but I'm not sure that it will work with your code. Specifically, the oclass variable is only created for instances of the class, not for the class itself. You could do it like this:
class socket(object):
oclass = otherclass()
#classmethod
def ping(cls):
return cls.oclass.send('PING')
socket.ping()
However, using your existing code and removing all decorators, you could simply instantiate it and use a method on the same line:
socket().ping()

Categories