PHPUnit and Guard Clause, not 100% coverage - php

I have this Guard:
protected function validateRemove($key)
{
if (!isset($this->collection[$key])) {
throw new CategoryCollectionBadItemException();
}
}
And the test:
/**
* #test
* #expectedException InfluenceDecision\Domain\Exception\Category\CategoryCollectionBadItemException
*/
public function removeMethodMustThrowExceptionWithInvalidKey()
{
$this->categoryCollection->add(
new Category(
null,
'test category'
)
);
$this->categoryCollection->remove(1);
}
CategoryCollection remove method calls validateRemove method
The test works fine, but the coverage isn't 100% because the test can't access to the last line of validateRemove method:
What's the propper solution?

That's happening because you are not testing both the branches of the function, in your test case the function breaks when you throw the exception so it's not technically finishing. You have tested half of the function, even if it's the only logic in there.
If you want the 100% coverage, you need to create a test where $this->collection[$key] is set .
In that case, I would suggest to change your function to something like
protected function validateRemove($key)
{
if (!isset($this->collection[$key])) {
throw new CategoryCollectionBadItemException();
}
return true;
}
and then create another test that asserts true when you call validateRemove() and $this->collection[$key] is set.
Mine is an example as I don't really know how do you use that piece of code!

Related

PHP returns object as null occasionally and unintentionally

We have PHP code in production that sometimes fails with "Call to member function on null", although the same code path executes fine several times before that in one invocation. We have a test that reproduces the error consistently at the same run of the loop.
I already proved that the object gets created correctly in the factory even if it gets returned as null. The factory method must not return null in any case, as indicated in the DocBlock. This question is not related to nullable return types or something like that.
The process does not exceed memory or runtime limitations and I already tried turning off the garbage collector, but no luck. The error happens both in PHP 7.0 and 7.3 on Debian, did not try on other versions or operating systems.
I am not allowed to paste the real code here, but I wrote a simple mockup to explain in more detail. Please keep in mind that this demo code will not result in the error, it is just meant to show the general structure of the program that runs into this fault.
// Three placeholder classes with common methods
class Bender
{
public function common()
{
echo "Bend, bend!" . PHP_EOL;
}
}
class Clamper
{
public function common()
{
echo "Clamp, clamp!" . PHP_EOL;
}
}
class Worker
{
public function common()
{
echo "Work, work!" . PHP_EOL;
}
}
// abstract class with static factory to produce objects
abstract class MomCorp
{
/**
* Factory to create one of several objects
*
* #param string $name
* #return Bender|Clamper|Worker
*/
public static function factory($name)
{
$type = self::managementDecision($name);
switch ($type)
{
case "bender":
$robot = new Bender();
break;
case "clamper":
$robot = new Clamper();
break;
default:
$robot = new Worker();
}
// optional QA works flawlessly here, object is fine all the time!
// $robot->common();
return $robot;
}
public static function managementDecision($name)
{
// irrelevant magic happens on $name here
return "bender";
}
}
foreach (['Rodriguez', 'Angle-ine', 'Flexo', 'Billie'] as $newName)
{
echo "$newName: ";
// The next two lines break after some loops - why?
// The perfectly functional object in factory gets returned as null
$bot = MomCorp::factory($newName);
$bot->common();
}
// SAMPLE OUTPUT
// Rodriguez: Bend, bend!
// Angle-ine: Bend, bend!
// Flexo: Bend, bend!
// Billie: Call to a member function common() on null
Has anyone experienced the same and has any hints on what might cause such an error and how to fix it?

PHPunit method expected to be called 1 time, actually called 0 times

I've been stuck on this for a while and I'm not sure why PHPunit can't see that the function is being called.
This is the code I'm trying to test:
public function handle()
{
$path = $this->request->getPath();
$requestMethod = $this->request->getMethod();
if (!$path) {
$this->redirect('home');
} else if (!$this->isMethodPathFound($path, $requestMethod)) {
$this->redirect('404');
} else {
$handler = $this->getControllerFullName($this->routes[$path]['handler']);
if (is_callable($handler)) {
call_user_func($handler);
} else {
$this->redirect('404');
}
}
}
/**
* #param string $path
* #param int $statusCode
*/
public function redirect($path, $statusCode = 303)
{
if (defined('TESTING_ENVIRONMENT') && TESTING_ENVIRONMENT) {
return;
}
header(
'Location: ' . $this->request->getProtocol() .
$this->request->getHost() . '/' . $path,
true,
$statusCode
);
die();
}
The TESTING_ENVIRONMENT variable is set for the header function so it does not trigger on running PHPunit (I don't want to create another class to have that redirect function just to be able to mock it for one test) and this is the testing code:
public function testHandlePathIsEmpty()
{
$requestMock = $this->getMockBuilder('\services\Request')->getMock();
$requestMock->expects($this->once())->method('getPath')->willReturn('');
$requestMock->expects($this->once())->method('getMethod')->willReturn('GET');
$routerMock = $this->getMockBuilder('\services\Router')
->setConstructorArgs([$this->routes, $requestMock])
->enableProxyingToOriginalMethods()
->getMock();
$routerMock->expects($this->once())->method('redirect')
->with('asdasd')->willReturn(true);
$routerMock->handle();
}
The $routerMock object should definitely invoke the "redirect" function, and it says that it does not get invoked..even though when I var_dump/die inside the function, it does go inside of it.
Thanks for the help!
Though you hesitated to show the complete output of phpunit's error, your problem is very likely not that your method is not called, but that it is not called with all the expectations you defined.
Your code
$routerMock->expects($this->once())->method('redirect')
->with('asdasd')->willReturn(true);
translates to the following expectations: The method redirect must be called exactly once with an argument 'asdasd' and will return true.
From your testcode I do not see that there is asdasd passed to the redirect method. Your test will most likely succeed when you remove the with expectation.
Just to make this clear. If you have to mock the class u want to test, your code is way to complex and you should think about implementing your logic in another way.
How about not mocking the class you are actually testing, create the new instance by passing the Request and a Router Mock (Router mock might not have any logic since you are not going to use it) and then do the following in your code:
public function handle()
{
$request = $this->request;
$path = $request->getPath();
if (!$path) {
$this->redirect('home');
} else if (!$this->isMethodPathFound($path, $request->getMethod())) {
$this->redirect('404');
} else {
$handler = $this->getControllerFullName($this->routes[$path]['handler']);
if (is_callable($handler)) {
call_user_func($handler);
} else {
$this->redirect('404');
}
}
}
In your Unit-Test, you now can just test for
$requestMock
->expects($this->never())
->method('getMethod');
I see that this would only cover the second case to not being executed but the third one could happen aswell. Thats always a point why your code is not clean enough.
You should read something about KISS and SOLID to make your code more testable. This method is just too complex as you could test it correctly.

PHPUnit Test result type or also the result variables

during unit testing i'm always get confused about what to test.
Do i need to test the API and only the API or also the method result values.
class SomeEventHandler
{
public function onDispatch (Event $event)
{
if ($event->hasFoo)
{
$model = $this->createResponseModel('foo');
}
else
{
$model = $this->createResponseModel('bar');
}
// End.
return $model;
}
private function createResponseModel ($foo)
{
$vars = array(
'someVare' => true,
'foo' => $foo
);
// End.
return new ResponseModel($vars);
}
}
So should i test if the method onDispatch returns a instance of ResponseModel or should i also test if the variable foo is set properly?
Or is the test below just fine?
class SomeEventHandlerTest
{
// assume that a instance of SomeEventHandler is created
private $someEventHandler;
public function testOnDispatch_EventHasFoo_ReturnsResponseModel ()
{
$e = new Event();
$e->hasFoo = true;
$result = $someEventHandler->onDispatch($e);
$this->assertInstanceOf('ResponseModel', $result);
}
public function testOnDispatch_EventHasNoFoo_ReturnsResponseModel ()
{
$e = new Event();
$e->hasFoo = false;
$result = $someEventHandler->onDispatch($e);
$this->assertInstanceOf('ResponseModel', $result);
}
}
If you were checking the code by hand what is it that you would check? Just that a ResponseModel was returned or that it also had the proper values?
If you weren't writing tests and executed the code what would you look for to ensure that the code was doing what it was supposed to. You would check that the values in the returned object were correct. I would do that by using the public API of the object and verify that the values are right.
One idea is to have the tests such that if the code were deleted, you would be able to recreate all the functionality via only having the tests. Only checking the returned object could result in a function that just has return new ResponseModel();. This would pass the test but would not be what you want.
In short, what you decide to test is subjective, however you should at the minimum test all your public methods.
Many people limit their tests to public methods and simply ensure code coverage on the protected/private methods is adequate. However, feel free to test anything you think warrants a test. Generally speaking, the more tests the better.
In my opinion you should certainly test for your response data, not just the return type.
I rely on Unit Tests to let me make code changes in the future and be satisfied my changes have not created any breaks, just by running the tests.
So in your case, if the "foo" or "bar" response data is important, you should test it.
That way if you later change the response strings by accident, your tests will tell you.

Build a UnitTest with multiple options on the same mocked-method

I have a problem to test a Method like that
public function index(){
if($this->request->is('get')){
if($this->Session->check('saveConflict')){
$this->set('conflict',true);
}else{
$this->set('data','test');
}
if($this->Session->check('full')){
$this->set('data',$this->Model->find('all'));
}else{
$this->set('data','test');
}
}else{
throw new BadRequestException;
}
}
unless that method maybe doesn't make sense, here is my problem. I have to call the method "check" on the Session-Component twice. But I want that for example the first methode mock-call retruns a "false" and the second a "true".
Here's what I have
$this->Editors->Session
->expects($this->once())
->method('check')
->will($this->returnValue(true));
I've tried it with the expectation "$this->at(1)" the call via order-index. But i think that isnt pretty clever because if I add a Session->check anywhere in the interpreted way though my base-method for example i have to change all those test-lines to make it work properly again.
I use CakePHP 2.4.6 and php-unit 4.1.3.
Is there any other why to do what I want to do?
Use the
->will($this->onConsecutiveCalls(array('return_value1',
'retur_value2',
...)));
Give the sequence of the return value in the order you want, the mocked method will return it in order.Or you can try the $this->returnCallback to do some sophisticated customize.You can find the example here How can I get PHPUnit MockObjects to return differernt values based on a parameter?.
Example
If you just want to do the unit test and cover all the path,I'll do like this:
public function testIndex()
{
....
$this->Editors->Session
->expects($this->any())
->method('check')
->will($this->returnCallback(array($this,'sessionCallback')));
$this->object->index();
$this->object->index();
.....
}
private $sessionFlag;
public function sessionCallback($value)
{
$rtnValue = $this->sessionFlag[$value];
$this->sessionFlag[$value] = (!$rtnValue);
return $rtnValue;
}

How do I write unit tests in PHP? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I've read everywhere about how great they are, but for some reason I can't seem to figure out how exactly I'm supposed to test something. Could someone perhaps post a piece of example code and how they would test it? If it's not too much trouble :)
There is a 3rd "framework", which is by far easier to learn - even easier than SimpleTest, it's called phpt.
A primer can be found here:
http://qa.php.net/write-test.php
Edit: Just saw your request for sample code.
Let's assume you have the following function in a file called lib.php:
<?php
function foo($bar)
{
return $bar;
}
?>
Really simple and straight forward, the parameter you pass in, is returned. So let's look at a test for this function, we'll call the test file foo.phpt:
--TEST--
foo() function - A basic test to see if it works. :)
--FILE--
<?php
include 'lib.php'; // might need to adjust path if not in the same dir
$bar = 'Hello World';
var_dump(foo($bar));
?>
--EXPECT--
string(11) "Hello World"
In a nutshell, we provide the parameter $bar with value "Hello World" and we var_dump() the response of the function call to foo().
To run this test, use: pear run-test path/to/foo.phpt
This requires a working install of PEAR on your system, which is pretty common in most circumstances. If you need to install it, I recommend to install the latest version available. In case you need help to set it up, feel free to ask (but provide OS, etc.).
There are two frameworks you can use for unit testing. Simpletest and PHPUnit, which I prefer. Read the tutorials on how to write and run tests on the homepage of PHPUnit. It is quite easy and well described.
You can make unit testing more effective by changing your coding style to accommodate it.
I recommend browsing the Google Testing Blog, in particular the post on Writing Testable Code.
I rolled my own because i didnt have time to learn someone elses way of doing things, this took about 20 minutes to write up, 10 to adapt it for posting here.
Unittesting is very usefull to me.
this is kinda long but it explains itself and there is an example at the bottom.
/**
* Provides Assertions
**/
class Assert
{
public static function AreEqual( $a, $b )
{
if ( $a != $b )
{
throw new Exception( 'Subjects are not equal.' );
}
}
}
/**
* Provides a loggable entity with information on a test and how it executed
**/
class TestResult
{
protected $_testableInstance = null;
protected $_isSuccess = false;
public function getSuccess()
{
return $this->_isSuccess;
}
protected $_output = '';
public function getOutput()
{
return $_output;
}
public function setOutput( $value )
{
$_output = $value;
}
protected $_test = null;
public function getTest()
{
return $this->_test;
}
public function getName()
{
return $this->_test->getName();
}
public function getComment()
{
return $this->ParseComment( $this->_test->getDocComment() );
}
private function ParseComment( $comment )
{
$lines = explode( "\n", $comment );
for( $i = 0; $i < count( $lines ); $i ++ )
{
$lines[$i] = trim( $lines[ $i ] );
}
return implode( "\n", $lines );
}
protected $_exception = null;
public function getException()
{
return $this->_exception;
}
static public function CreateFailure( Testable $object, ReflectionMethod $test, Exception $exception )
{
$result = new self();
$result->_isSuccess = false;
$result->testableInstance = $object;
$result->_test = $test;
$result->_exception = $exception;
return $result;
}
static public function CreateSuccess( Testable $object, ReflectionMethod $test )
{
$result = new self();
$result->_isSuccess = true;
$result->testableInstance = $object;
$result->_test = $test;
return $result;
}
}
/**
* Provides a base class to derive tests from
**/
abstract class Testable
{
protected $test_log = array();
/**
* Logs the result of a test. keeps track of results for later inspection, Overridable to log elsewhere.
**/
protected function Log( TestResult $result )
{
$this->test_log[] = $result;
printf( "Test: %s was a %s %s\n"
,$result->getName()
,$result->getSuccess() ? 'success' : 'failure'
,$result->getSuccess() ? '' : sprintf( "\n%s (lines:%d-%d; file:%s)"
,$result->getComment()
,$result->getTest()->getStartLine()
,$result->getTest()->getEndLine()
,$result->getTest()->getFileName()
)
);
}
final public function RunTests()
{
$class = new ReflectionClass( $this );
foreach( $class->GetMethods() as $method )
{
$methodname = $method->getName();
if ( strlen( $methodname ) > 4 && substr( $methodname, 0, 4 ) == 'Test' )
{
ob_start();
try
{
$this->$methodname();
$result = TestResult::CreateSuccess( $this, $method );
}
catch( Exception $ex )
{
$result = TestResult::CreateFailure( $this, $method, $ex );
}
$output = ob_get_clean();
$result->setOutput( $output );
$this->Log( $result );
}
}
}
}
/**
* a simple Test suite with two tests
**/
class MyTest extends Testable
{
/**
* This test is designed to fail
**/
public function TestOne()
{
Assert::AreEqual( 1, 2 );
}
/**
* This test is designed to succeed
**/
public function TestTwo()
{
Assert::AreEqual( 1, 1 );
}
}
// this is how to use it.
$test = new MyTest();
$test->RunTests();
This outputs:
Test: TestOne was a failure
/**
* This test is designed to fail
**/ (lines:149-152; file:/Users/kris/Desktop/Testable.php)
Test: TestTwo was a success
Get PHPUnit. It is very easy to use.
Then start with very simple assertions. You can do alot with AssertEquals before you get into anything else. That's a good way to get your feet wet.
You may also want to try writing your test first (since you gave your question the TDD tag) and then write your code. If you haven't done this before it is an eye-opener.
require_once 'ClassYouWantToTest';
require_once 'PHPUnit...blah,blah,whatever';
class ClassYouWantToTest extends PHPUnit...blah,blah,whatever
{
private $ClassYouWantToTest;
protected function setUp ()
{
parent::setUp();
$this->ClassYouWantToTest = new ClassYouWantToTest(/* parameters */);
}
protected function tearDown ()
{
$this->ClassYouWantToTest = null;
parent::tearDown();
}
public function __construct ()
{
// not really needed
}
/**
* Tests ClassYouWantToTest->methodFoo()
*/
public function testMethodFoo ()
{
$this->assertEquals(
$this->ClassYouWantToTest->methodFoo('putValueOfParamHere), 'expectedOutputHere);
/**
* Tests ClassYouWantToTest->methodBar()
*/
public function testMethodFoo ()
{
$this->assertEquals(
$this->ClassYouWantToTest->methodBar('putValueOfParamHere), 'expectedOutputHere);
}
For simple tests AND documentation, php-doctest is quite nice and it's a really easy way to get started since you don't have to open a separate file. Imagine the function below:
/**
* Sums 2 numbers
* <code>
* //doctest: add
* echo add(5,2);
* //expects:
* 7
* </code>
*/
function add($a,$b){
return $a + $b;
}
If you now run this file through phpdt (command-line runner of php-doctest) 1 test will be run. The doctest is contained inside the < code > block. Doctest originated in python and is fine for giving useful & runnable examples on how the code is supposed to work. You can't use it exclusively because the code itself would litter up with test cases but I've found that it's useful alongside a more formal tdd library - i use phpunit.
This 1st answer here sums it up nicely (it's not unit vs doctest ).
phpunit is pretty much the defacto unit testing framework for php. there is also DocTest (available as a PEAR package) and a few others.
php itself is tested for regressions and the like via phpt tests which can also be run via pear.
Codeception tests are much like common unit tests but are much powerful in things where you need mocking and stubbing.
Here is the sample controller test. Notice how easily stubs are created. How easily you check the method was invoked.
<?php
use Codeception\Util\Stub as Stub;
const VALID_USER_ID = 1;
const INVALID_USER_ID = 0;
class UserControllerCest {
public $class = 'UserController';
public function show(CodeGuy $I) {
// prepare environment
$I->haveFakeClass($controller = Stub::makeEmptyExcept($this->class, 'show'));
$I->haveFakeClass($db = Stub::make('DbConnector', array('find' => function($id) { return $id == VALID_USER_ID ? new User() : null ))); };
$I->setProperty($controller, 'db', $db);
$I->executeTestedMethodOn($controller, VALID_USER_ID)
->seeResultEquals(true)
->seeMethodInvoked($controller, 'render');
$I->expect('it will render 404 page for non existent user')
->executeTestedMethodOn($controller, INVALID_USER_ID)
->seeResultNotEquals(true)
->seeMethodInvoked($controller, 'render404','User not found')
->seeMethodNotInvoked($controller, 'render');
}
}
Also there are other cool things. You can test database state, filesystem, etc.
Besides the excellent suggestions about test frameworks already given, are you building your application with one of the PHP web frameworks that has automated testing built in, such as Symfony or CakePHP? Sometimes having a place to just drop in your test methods reduces the start-up friction some people associate with automated testing and TDD.
Way too much to re-post here, but here is a great article on using phpt. It covers a number of aspects around phpt that are often overlooked, so it could be worth a read to expand your knowledge of php beyond just writing a test. Fortunately the article also discusses writing tests!
The main points of discussion
Discover how marginally documented aspects of PHP work (or pretty much any part for that matter)
Write simple unit tests for your own PHP code
Write tests as part of an extension or to convey a potential bug to the internals or QA groups
I know there is a lot of info here already, but since this still shows up on Google searches i might as well add Chinook Test Suite to the list. It is a simple and small test framework.
You can easily test your classes with it and also create mock objects. You run the tests through a web browser and (not yet) through a console.
In the browser you can specify what test class or even what test method to run. Or you can simply run all tests.
A screenshot from the github page:
What i like about it is the way you assert tests. This is done with so called "fluent assertions". Example:
$this->Assert($datetime)->Should()->BeAfter($someDatetime);
And creating mock objects is a breeze too (with a fluent like syntax):
$mock = new CFMock::Create(new DummyClass());
$mock->ACallTo('SomeMethod')->Returns('some value');
Anyway, more info can be found on the github page with a code example as well:
https://github.com/w00/Chinook-TestSuite

Categories