Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I mock a method called within the constructor in a PHP unit test?

I'm having trouble unit testing a class that has a method called within the constructor. I don't understand how to mock this. Perhaps I should use the 'setUp' method of phpUnit?

I'm using the Mockery library. Is there is a better tool than this?

class ToTest
{

   function __construct() {

       $this->methodToMock(); // need to mock that for future tests 

   }

   // my methods class

}

Any suggestions would be appreciated.

like image 971
Fabrizio Fenoglio Avatar asked Apr 15 '14 08:04

Fabrizio Fenoglio


2 Answers

If you class is difficult to instantiate to test, that is a code smell that your class is doing too much or doing work in the constructor.

http://misko.hevery.com/code-reviewers-guide/

Flaw #1: Constructor does Real Work

Warning Signs

  • new keyword in a constructor or at field declaration
  • Static method calls in a constructor or at field declaration
  • Anything more than field assignment in constructors
  • Object not fully initialized after the constructor finishes (watch out for initialize methods)
  • Control flow (conditional or looping logic) in a constructor
  • Code does complex object graph construction inside a constructor rather than using a factory or builder
  • Adding or using an initialization block

Whatever your methodToMock function does in your constructor needs to be rethought. As mentioned in the other answers, you probably want to use dependency injection to pass in things that your class is doing.

Rethink what your class is actually doing and refactor so that it is easier to test. This also has the benefit of making your class easier to reuse and modify later on.

like image 139
Schleis Avatar answered Oct 11 '22 05:10

Schleis


The problem here is that the method can not be mocked as the object is not yet instantiated. sectus answer is valid but maybe not very flexible, as it can be difficult to change the behavior of the mocked method on different tests.

You can create another class that does the same as the method you want to mock, and have an instance of that class passed as a constructor argument. That way you can pass a mock class on your test. Usually the problem you're having is a smell of a class doing too many things.

like image 33
gontrollez Avatar answered Oct 11 '22 03:10

gontrollez