I'm new to php unit testing, What are the valid test cases for below function.
protected function validateParams($graph, $start, $destination)
{
if (!is_object($graph)) {
throw new \InvalidArgumentException('Graph param should be an object !');
}
if (empty($start)) {
throw new \InvalidArgumentException('Start param is empty !');
}
if (empty($destination)) {
throw new \InvalidArgumentException('Graph param is empty !');
}
return true;
}
First, test that when passing the correct arguments to the method, it returns true.
public function testParamsValidation();
Then check that an InvalidArgumentException is thrown when any of the arguments is empty. Note that you should have 3 tests, one for each of the arguments. In each test you pass only one empty argument. You probably want to have each of these tests executed several times with distinct argument values (like null, false, scalars, etc). Use dataProviders for that.
public function testInvalidArgumentExceptionIsThrownWhenGraphIsNotAnObject(;
public function testInvalidArgumentExceptionIsThrownWhenStartIsEmpty();
public function testInvalidArgumentExceptionIsThrownWhenDestinationIsEmpty();
Side note: you probably want to explicit the required object class in the method definition. The $graph object should be of a certain class or implement a certain interface?
Related
I am trying to unit test a function which is in an entity class, and it is stored in my DB by the use of a constructor. Each time I am trying to test this function it is giving me that error
ArgumentCountError: Too few arguments to function App\Entity\Deal::__construct(), 0 passed in /var/www/html/casus/tests/dealsEntityFunctionsTest.php on line 10 and exactly 1 expected
It is obvious I think, but I am really new with unit testing and that stuff so I couldn't find the answer. Could you please help me?
My code is
class Deal
{
private bool $isNewToday
public function __construct($deal)
{
$this->isNewToday = $deal['is_new_today'];
}
public function getIsNewToday(): ?bool
{
return $this->isNewToday;
}
public function setIsNewToday(bool $isNewToday): self
{
$this->isNewToday = $isNewToday;
return $this;
}
}
And my unit test is
class test extends TestCase
{
public function testIsNewTodayIsTrue()
{
$deal = new Deal();
$deal->setIsForSale(true);
$this->assertTrue($deal->getIsForSale(), true);
}
}
As brombeer suggested, new Deal entity requires parameter.
This parameter looks like an array, with key 'is_new_today'. So, sth like this below should help with constructor error.
class test extends TestCase
{
public function testIsNewTodayIsTrue()
{
$deal = new Deal(['is_new_today' => true]);
$deal->setIsForSale(true);
$this->assertTrue($deal->getIsForSale(), true);
}
}
This has nothing to do with Unit Testing, or Symfony, or any of the other details you mentioned. You've defined something with a mandatory parameter, and then aren't passing that parameter.
Just like any function, the parameters to a constructor are mandatory unless you provide a default. And if you write code that assumes the parameter will have a particular format, you need to provide a value that meets that assumption.
So either pass the parameter every time you create the object, with whatever format the constructor expects:
$deal = new Deal(['is_new_today' => false]);
... or make it optional, and decide what should happen if it's not passed:
class Deal
{
private bool $isNewToday
public function __construct(?array $deal = null)
{
if ( isset($deal) ) {
$this->isNewToday = $deal['is_new_today'];
}
else {
$this->isNewToday = false;
}
}
}
Note that $isNewToday is defined as a non-nullable boolean, so you should always give it a value in the constructor, or an inline default, like private bool $isNewToday = false; Otherwise, you'll get "uninitialized value" errors if you try to read it. For that reason, the return type of ?bool on getIsNewToday() doesn't make sense - it can't return null, because $this->isNewToday can never be bool.
i create my own container, i made"bind" method that get "string $alias" and "Closure $closure". the method bind the alias to the object on the $container array , like that:
public function bind(string $alias,$closure)
{
$this->container[$alias] = $closure();
}
The second method is "call", that simply call to some instance from the $container. Of course first the method check if the given alias exists, and if doesnt throw an exception
public function call(string $alias)
{
if(array_key_exists($alias,$this->container))
return $this->container[$alias];
throw new \Exception();
}
is that good ? what more i need to add ? cause i saw on laravel for example that the container is full of method and props, and i dont know why. its just bind and call, isnt ?
My suggestion is to do the following:
Bind
public function bind(string $alias,$closure)
{
$this->container[$alias] = $closure;
}
Call (new instance per call)
public function call(string $alias)
{
if(array_key_exists($alias,$this->container)) {
$closure = $this->container[$alias];
return $closure();
}
throw new \Exception();
}
Call (singleton behaviour)
public function call(string $alias)
{
if (array_key_exists($alias,$this->instanceContainer)) {
return $this->instanceContainer[$alias];
}
if(array_key_exists($alias,$this->container)) {
$closure = $this->container[$alias];
$this->instanceContainer[$alias] = $closure();
return $this->instanceContainer[$alias];
}
throw new \Exception();
}
Which one of the two implementations you pick for the call will depend on your needs. If you need to call the function only once per object go for the second one. You could also have 2 methods: bind and bindSingleton to differentiate the two different binding types.
There are two main reasons I recommend this:
The object is generated on demand, which means that if you bind 1000 objects but only use 10 you'll only create 10 objects.
The result of the $closure() method may be different based on the time it was called. For example, if you want to instantiate an object which contains a time-stamp, instantiating it on call rather than on bind is more reasonable.
class Testme()
{
public function testMe ($a)
{
if ($a == 1)
{
throw new Exception ('YAY');
}
}
}
so its easy to test if it threw exception
/**
* #expectedException Exception
*/
public function test()
{
new Testme(1);
}
but what if it didn't do anything?
public function test()
{
new Testme(2);
?? ? ? ? ?
}
Scenarios
You have two possible scenarios for a function to do nothing:
Scenario 1: No return statement
Your function does nothing because you do not perform actions in it and you do not include the return keyword in it:
public function doNothing()
{
// Do nothing.
}
Scenario 2: With return statement
Your function does nothing because you do not perform actions in it and you do include the return keyword in it without expressing any return value:
public function doNothing()
{
// Do nothing.
return;
}
Other scenarios
I will leave out of the cases to treat the following scenarios:
Case in which you do not return anything but you perform significant actions that can be tested on other objects. In this case you must unit-test the resulting states of the modified objects.
Case in which you do nothing but return something, then you should unit-test the return value.
Exploring the documentation in the PHP manual
For the first case, the PHP manual documents that the evaluated expression of the function will be null. It says here: http://php.net/manual/en/functions.returning-values.php in a note:
If the return is omitted the value NULL will be returned.
For the second case, the PHP manual documents that the evaluated expression of the funcion will also be null. It says here: http://php.net/manual/en/function.return.php in a note:
If no parameter is supplied, then the parentheses must be omitted and NULL will be returned. [...]
Conclusion
It is therefore clearly documented that a function that "does nothing" necessarily evaluates to null.
How to test a function that does nothing
Just assert your expectations:
$this->assertNull( $sut->doNothing() );
This way you "exercise" your function, you run over it making the code-coverage complete all the lines, and you "expect" that "nothing happened" by testing the null value of its evaluation as an expression, as documented.
How to test a constructor that does nothing
Nevertheless to test a constructor... well... common sense: What's the purpose of a constructor? Create an object (instance) of a certain type (class), right?
So... I prefer to start the 100% of my unit tests by checking that the $sut has been created. This is the VERY first test I write when I'm writing the code of a new class. This is the test I write even before the class exists. At the end, this is what the constructor is for. Red bar. Then I create the class. Green bar.
Let's say I have an Email class that takes a string and will be only created if a valid email is passed and throws exception otherwise. this is very similar to your question. A constructor that just "allows the creation" or "denies it by exploding the system".
I usually would do something like this:
//-------------------------------------------------//
// Tests //
//-------------------------------------------------//
/** #dataProvider validEmailProvider **/
public function testCreationIsOfProperClass( string $email )
{
$sut = $this->getSut( $validEmail );
$this->assertInstanceOf( Email::class, $sut );
}
/** #dataProvider invalidEmailProvider **/
public function testCreationThrowsExceptionIfEmailIsInvalid( string $invalidEmail )
{
$this->expectException( EmailException::class );
$this->getSut( $invalidEmail );
}
//-------------------------------------------------//
// Data providers //
//-------------------------------------------------//
public function validEmailProvider() : array
{
return
[
[ 'alice#example.com' ],
[ 'bob.with-several+symbols#subdomain.another.subdomain.example.verylongTLD' ],
]
}
public function invalidEmailProvider() : array
{
return
[
[ 'missing_at_symbol' ],
[ 'charlie#cannotBeOnlyTld' ],
]
}
//-------------------------------------------------//
// Sut creators //
//-------------------------------------------------//
private function getSut( string $email ) : Email
{
return new Email( $email );
}
As I use PHP 7.0 and I put types everywhere, both entering the parameters and also in the return types, if the created object was not an Email, the getSut() function would fail first.
But even if I wrote it omitting the return type, the test tests what it is expected to happen: new Email( 'valid#example.com' ); is itself an expression that shoud evaluate to "something" of class Email::class.
How to test a constructor that does something
Code smell. The constructor probably should not do work. If any, just store parameters. If the constructor "does work" other than storing parameters consider lazy-processing on getters, or delegating that work in a factory or so.
How to test a constructor that "does nothing but store parameters"
Just like before + then get the data.
Test in your first test that the creation is an instance of something.
Then in another different test, exercise something like a getter that gets you what entered in the constructor even if the constructor did not anything (other than storing it).
Hope that this helps.
In PHPUnit 7.2+ you can also use TestCase::expectNotToPerformAssertions()
public function test()
{
// ...
$this->expectNotToPerformAssertions();
}
This has the same behaviour as the #doesNotPerformAssertions annotation.
2018+
Nowadays the best practice is annotation exactly for these cases:
/**
* #doesNotPerformAssertions
*/
public function testSomething()
{
$someService = new SomeObject();
$someService->shallNotFail();
}
Example pull-request
PHPUnit documentation (poor)
It's not possible. Add return statement and assert the result.
class Testme()
{
public function testMe ($a)
{
if ($a == 1)
{
throw new Exception ('YAY');
}
return true;
}
}
and then
$object = new Testme();
$this->assertTrue($object->testMe(2));
Note: The credits for this solution go to this related answer. The context may seem a little different, but the solution / workaround works the same way. Testing that an exception is not thrown is just the same as testing a method with no return value.
According to this issue thread, there is no built in solution for testing something like DoesNotThrowException in PHPUnit (yet).
So yes, one solution would be to return some dummy value from your method, like
public function testMe ($a)
{
if ($a == 1) { throw new Exception ('YAY'); }
return true;
}
and then assert it in your test. But if you don't want to change the code just for the test, you can work around it:
public function testExceptionIsNotThrown()
{
try {
new Testme(2);
}
catch(Exception $e) {
/* An exception was thrown unexpectedly, so fail the test */
$this->fail();
}
/* No exception was thrown, so just make a dummy assertion to pass the test */
$this->assertTrue(true);
}
It may seem hacky and not very intuitive, but if it's stupid but it works, it's not stupid.
This is an very interesting question, although lot of answers were written, none of them seems to properly answer the question, since you have asked using the class let me explain this way.
Please keep in mind that an instance method you have created in class should have only 2 intentions.
It can alter the state of a class ( change the class properties like private variables )
It returns the state of the class ( getters )
any thing other than this is meaningless unless it is a static method. for example
if you have class like this
class Foo {
private $prop = null;
public function fooMethod() {
$this->prop = "string";
}
public function getProp() {
return $this->prop;
}
}
the method fooMethod() does not return any thing, but it affects the state of $prop property in the class, you can test the method by
$this->assertNotNull( $instance->getProp() );
because you knew if this method is run then the prop $prop should be affected and state of that variable is changed.
Miscellanous Scenario: My method doesn't alter the state and also won't return any state variables.
Then the method is static. It should not be an instance method, and the static methods usually have return type, because they cant affect the state of the class and also can't return state variables. This constraints the static methods from storing a result somewhere (unless you store them globals, don't do that ), so it should definitely return some output. If you don't want to return output, then you could consider returning a boolean from static method.
public function testThrowingException()
{
$this->expectException(Exception::class);
$this->expectExceptionMessage('YAY');
(new Testme())->testMe(1);
}
public function testNotThrowingException()
{
$this->expectNotToPerformAssertions();
(new Testme())->testMe(2);
}
I stumled upon the same problem. To ensure "nothing" has happened it's enough to just call you the method in your unit test. If it fails the test will fail anyway.
If you just call your method without the #expectedException annotation like this
public function test()
{
new Testme(1);
}
you'll get an error
There was 1 error:
1) Testme::testMe
Exception: YAY
I have a class in php that works with the chainning method, but the problem is that I want to chain the methods in some order.
class Chain {
public function foo () {
return $this;
}
public function bar () {
return $this;
}
public function some () {
return $this;
}
}
So, if I use this class, then I can chain this methods in 9 different ways (all the possible combinations of 3 elements)
But what happen if I determine that the method some always must to be chained after foo or bar and not in other way?
$chain = new Chain();
$chain->foo->bar(); //works; i.e: the method some is optional
$chain->foo()->bar()->some(); //works
$chain->bar()->foo()->some(); //works
$chain->some()->bar()->foo(); //throws an exception
I think that I can do this setting boolean values, something like: when the method foo or bar are called, then I set the value to some var to true, and when the developer calls the some function, if that var is false, then throws an exception, otherwise is allowed to continue.
But I need something more elegant, such as pattern or a built-in solution.
There is another way to do it?
The very rough example I imagine will still have some lines of code in each method
<?php
class Chain {
private $_register = array();
public function foo () {
$this->register(__METHOD__);
return $this;
}
public function bar () {
$this->register(__METHOD__);
return $this;
}
public function some () {;
$this->verify('foo'); // foo() should be called before some();
$this->register(__METHOD__);
echo 'it\'s ok';
return $this;
}
public function verify($method) {
if(array_key_exists($method, $this->_register) && $this->_register[$method] == true) {
return true;
}
else {
throw new Exception('Some exception');
}
}
public function register($method) {
$method = str_replace(__CLASS__.'::', '', $method);
$this->_register[$method] = true;
}
}
What do we do here - we have a register() and verify() methods. (they can be helpers, but for the current purpose I added them in the class.
Each method should have before it's returning value a register to itself. Calling $this->register(__METHOD__) from foo() will add in the private array 'foo' => true.
The verify() method checks if foo exist as array key and if its value is true. If it is - the script will continue. Otherwise - throws exception.
In this case:
$chain = new Chain();
$chain->bar()->some()->foo(); //throws an exception
Fatal error: Uncaught exception 'Exception' with message 'Some
exception' in ...
$chain = new Chain();
$chain->foo()->some()->foo(); // ok
it's ok
The problem here is that we establish a "convention". You need to pass __METHOD__ to the register function so after it replace the classname it will add only the method name in the array. So later, in the function where you need to verify if one or more functions are called before this, you need to use the method name as string i.e. $this->verify('foo');
Ofcourse you can play different scenarios without stripping and testing with strpos() or adding () after the methodname for easier recognition if you are verifying a method or smth else.
But at least it will save you from making for each method, different variable to fill i.e.
function foo() {
$this->_foo = true;
return $this;
}
function bar() {
$this->_bar = true;
return $this;
}
Forcing the caller to stick to a certain order of calls just as an end to itself is hardly useful at all. Supposedly what you're really interested in is to make sure the state of the object is valid when you call some() and throw an exception if it's not. In that case, yes, you would check certain indicators of your object's state and throw an exception when this state does not fulfil the requirements that some() may be called. As a concrete example:
$api = new SomeAPI;
$api->setUserID($id);
$api->setSecretKey($secret);
$api->call('something');
Here call() would check that the user id and access key has been set, otherwise it can't do its job. Whether these calls are chained or not is irrelevant and just a syntactic detail.
Alternatively, you could return certain objects of other (sub) classes from your methods which physically make it impossible to call certain methods on them if certain conditions haven't been met:
public function bar() {
if ($this->foo) {
return new SubFoo($this->foo);
} else {
return new SubBar;
}
}
This may be overly complicated though.
With PHPUnit, I am testing a sequence of method calls using ->at(), like so:
$mock->expects($this->at(0))->method('execute')->will($this->returnValue('foo'));
$mock->expects($this->at(1))->method('execute')->will($this->returnValue('bar'));
$mock->expects($this->at(2))->method('execute')->will($this->returnValue('baz'));
How can I set up the mock so that, in the above scenario, if execute() is called four or more times, it will immediately fail? I tried this:
$mock->expects($this->at(3))->method('execute')->will($this->throwException(new Exception('Called too many times.')));
But this also fails if execute() is not called four times. It needs to fail immediately, otherwise the system under test will produce errors of its own, which causes the resulting error message to be unclear.
I managed to find a solution in the end. I used a comination of $this->returnCallback() and passing the PHPUnit matcher to keep track of the invocation count. You can then throw a PHPUnit exception so that you get nice output too:
$matcher = $this->any();
$mock
->expects($matcher)
->method('execute')
->will($this->returnCallback(function() use($matcher) {
switch ($matcher->getInvocationCount())
{
case 0: return 'foo';
case 1: return 'bar';
case 2: return 'baz';
}
throw new PHPUnit_Framework_ExpectationFailedException('Called too many times.');
}))
;
For special cases like this, I typically use something like the following:
public function myMockCallback() {
++$this -> _myCounter;
if( $this -> _myCounter > 3 ) {
// THROW EXCEPTION OR TRIGGER ERROR
}
... THEN YOUR CASE STATEMENT OR IF/ELSE WITH YOUR CHOICE OF RETURN VALUES
}
... INSIDE TEST FUNCTION ....
$mockObject ->expects($this->any())
->method('myMethod')
->will($this->returnCallback( array ($this, 'myMockCallback' )));
You could separate test to 2 dependent methods, using #depends annotation.
In this case your first test only tests that there are exact 3 method executions, and second - other logic.
What about using data providers?
class MyTest extends PHPUnit.... {
/**
* #var const how much till throwing exception
*/
const MAX_EXECUTE_TILL_EXCEPTION = 3;
public function setUp(){}
public function tearDown(){}
/**
* #dataProvider MyExecuteProvider
*/
pulbic function testMyExecuteReturnFalse($data){
$mock = //setup your mock here
//if using "$ret" doesn't work you cant just call another private helper that will decide if you need to
// return value or throwing exception
if (self::MAX_EXECUTE_TILL_EXCEPTION == $data){
$ret = $this->throwException(new Exception('Called too many times.'));
} else {
$ret = $this->returnValue('foo');
}
$mock->expects($this->at($data))->method('execute')->will($ret);
}
public function MyExecuteProvider(){
return array(
0,1,2,3
)
}
}
This is just another idea, and I think that zerkms suggested very good idea as well