40
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);
 ?? ? ? ? ?
}
John Smith
  • 6,129
  • 12
  • 68
  • 123
  • 13
    Please do not downvote silently! I think this is a perfectly valid question, so please explain if you disagree. @OP: This is a related question: http://stackoverflow.com/questions/27511593/how-to-phpunit-test-a-method-with-no-return-value – Quasdunk Feb 22 '15 at 18:16
  • 1
    its OK to return with a value - but if my original method is for to "do" something, not to "give" something, I dont want to change the original code – John Smith Feb 22 '15 at 18:18
  • 2
    Not sure what exactly you mean by that... The solution in the related question is to wrap the function call in your test in a try-catch-block. If an exception is thrown, you catch it and fail the test manually (`$this->fail()`), otherwise just make a dummy assertion that passes the test (`$this->assertTrue(true)`). You don't test if anything is returned or not, you just focus on the exception. – Quasdunk Feb 22 '15 at 18:24
  • 1
    And just to make it explicitly clear: There is no built-in solution for that (yet). There is a GitHub issue thread discussing the details: https://github.com/sebastianbergmann/phpunit-documentation/issues/171 – Quasdunk Feb 22 '15 at 18:31

8 Answers8

27

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
    [
        [ '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.

  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.

Xavi Montero
  • 9,239
  • 7
  • 57
  • 79
  • 2
    Indeed it does work, but please note that if the method is using a PHP 7.1 "void" return type, it still works but will be reported by many static analysis tools as a "void method result used". – Pierre-Yves Nov 15 '17 at 17:06
  • Great answer, but: "What's the purpose of a constructor? Create a class of a certain type, right?" Actually a constructor instantiates an object of a certain class/type. – alexg Jun 14 '19 at 13:07
  • Yes, your subtle correction is true. The constructor does not "create a class", but "creates an instance of a certain class". That's true. I'm going to edit the answer to incorporate your contribution. Thanks for your contrib!! – Xavi Montero Jun 15 '19 at 11:34
  • This complicated approach is not needed from PHPUnit 7.1, 2018. See my answer bellow. – Tomas Votruba Dec 16 '20 at 17:34
20

In PHPUnit 7.2+ you can also use TestCase::expectNotToPerformAssertions()

public function test()
{
    // ...

    $this->expectNotToPerformAssertions();
}

This has the same behaviour as the @doesNotPerformAssertions annotation.

Nicholas Betsworth
  • 1,707
  • 19
  • 24
11

2018+

Nowadays the best practice is annotation exactly for these cases:

/**
 * @doesNotPerformAssertions
 */
public function testSomething()
{
    $someService = new SomeObject();
    $someService->shallNotFail();
}
Tomas Votruba
  • 23,240
  • 9
  • 79
  • 115
  • 3
    The documentation there does **not** state that this annotation is "for testing that a method does nothing", **but** for avoiding warning messages, which is always a bad practice. Literally it says "@doesNotPerformAssertions Prevents a test that performs no assertions from being considered risky.". The question does **not** ask "how can I execute code without testing anything" **but** "how can I test that it does nothing", which is different. Make reportings to be "silenced" should be used temporarily only to disable verbosity when we have something half-coded, not as the definitive behaviour. – Xavi Montero Dec 17 '20 at 09:35
  • 1
    Thanks for reaching out. Your comment is hard for me to understand. If we look just at the input code in the question (without refactoring it), the `@doesNotPerformAssertions` is exactly the way to test it. – Tomas Votruba Dec 17 '20 at 10:54
  • 1
    In my answer I state the case of the source in the question in the section "How to test a constructor that does nothing". The `new` operator indeed "does" something: Creates a new instance of the given class. That said, the test is `$this->assertInstanceOf( HappyClass::class, new HappyClass( $whatever );` – Xavi Montero Dec 17 '20 at 21:20
  • 1
    The question is rather generic "Phpunit, how to test if method does “nothing”?" Athought your answer is technically right, people coming from google looks for answer for their own code. The answer should help not only the exact detailed code of the asker, but all his followers. `@doesNotPerformAssertions` stands strong there – Tomas Votruba Dec 18 '20 at 00:12
3

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));
Brejk
  • 454
  • 3
  • 12
3

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.

Community
  • 1
  • 1
Quasdunk
  • 14,944
  • 3
  • 36
  • 45
2
public function testThrowingException()
{
    $this->expectException(Exception::class);
    $this->expectExceptionMessage('YAY');
    (new Testme())->testMe(1);
}

public function testNotThrowingException()
{
    $this->expectNotToPerformAssertions();
    (new Testme())->testMe(2);
}
Chyngyz Sagynov
  • 151
  • 2
  • 5
1

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.

  1. It can alter the state of a class ( change the class properties like private variables )
  2. 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.

Dip Hasan
  • 225
  • 3
  • 9
Naveen
  • 769
  • 1
  • 9
  • 28
-1

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
totas
  • 10,288
  • 6
  • 35
  • 32