Another day, another PHPUnit revelation. Yesterday some code started returning an internal server error. In true developer fashion, I ran the same process several times and was absolutely staggered to find that the same thing happened every time. No amount of hard refreshing or cache clearing would help. I considered phoning the server hosting company to ask if someone had spilt a coffee somewhere they shouldn’t have, but begrudgingly accepted that it was possibly a coding error. A look at the logs confirmed my suspicions. An email being generated at the end of the process included a call to a method that no longer existed.
“But wait!”, I hear you cry (possibly followed by, are you allowed to start a paragraph with the word “But”?), you claim to be an advocate of PHPUnit testing. How could you possibly have code with calls to methods that no longer exist that doesn’t fail unit tests? Well the short answer is that the code in question generates an email and at the point I was writing tests for the class I was still in the murky days before I discovered PHPUnit stubs and mocks. As a result, I coughed loudly and skipped over the methods in question when writing tests. Luckily I am now a changed man, and ready for the challenge.
I moved the code that sends the mail to a separate method, which I should have done anyway. To be fair, the code I was refactoring was from the glory days of writing methods that perform ten tasks at once. Then it was on to writing tests. Of the three possible emails that get generated, two were straight forward to stub and test:
$test = $this->getMockBuilder( 'Class_That_Mails' ) ->setMethods( [ 'send_mail' ] ) ->getMock(); $test->expects( $this->once() ) ->method( 'send_mail' ) ->will( $this->returnValue( true ) ); // Run tests.
However, the third of the three methods generates two separate mails to two separate people. Yes, I should separate them into two methods, but I didn’t because I’m wild like that. All good until I want to define how many times to expect the method to run.
$test->expects( $this->twice() ) is not a valid method (probably for the best as I’m not sure what comes after “thrice”). Fortunately we can use the “exactly” method (presumably written after the PHPUnit developers also struggled to find out what follows thrice).
So to cut a short story long, here’s the stub for a method that runs twice (simply replace the “2” with the number of your choice should yours involved methods being called more than twice).
$test = $this->getMockBuilder( 'Class_That_Mails' ) ->setMethods( [ 'send_mail' ] ) ->getMock(); $test->expects( $this->exactly( 2 ) ) ->method( 'send_mail' ) ->will( $this->returnValue( true ) ); // Run tests.