Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Phpunit, how to test if method does "nothing"?

Tags:

php

phpunit

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);  ?? ? ? ? ? } 
like image 795
John Smith Avatar asked Feb 22 '15 18:02

John Smith


People also ask

How do I run a PHPUnit test?

How to Run Tests in PHPUnit. You can run all the tests in a directory using the PHPUnit binary installed in your vendor folder. You can also run a single test by providing the path to the test file. You use the --verbose flag to get more information on the test status.

What is assertion in PHPUnit?

The assertion methods are declared static and can be invoked from any context using PHPUnit\Framework\Assert::assertTrue() , for instance, or using $this->assertTrue() or self::assertTrue() , for instance, in a class that extends PHPUnit\Framework\TestCase .


1 Answers

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:

  1. 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.

  2. 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     [         [ '[email protected]' ],         [ '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( '[email protected]' ); 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.

  1. Test in your first test that the creation is an instance of something.
  2. 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.

like image 130
Xavi Montero Avatar answered Sep 22 '22 19:09

Xavi Montero