Is it bad to mock the test object in unit test?

Here is the class I'm testing. I am currently testing the doSomething function:

 class FooClass { public function doSomething( $user ) { $conn = $this->getUniqueConnection( $user->id ); $conn->doSomethingDestructive(); } private function getUniqueConnection( $id ) { return new UniqueConnection( $id ); } } 

As you can see, the doSomething function receives a new instance of UniqueConnection (a class that I am not testing here) based on the property of the argument it receives. The problem is that the UniqueConnection:: doSomethingDestructive is what I cannot name during the tests because of its ... destructiveness. So I would like to UniqueConnection / mock UniqueConnection , and not use the real one.

I see no way to introduce my mock UniqueConnection . I would make the argument of the UniqueConnection constructor for FooClass , but, as you can see, a new one is created based on the parameter of the doSomething function, and all the unique identifiers with which it can be called are unknown beforehand.

My only option I see is to check the layout of FooClass instead of FooClass . Then I would replace the getUniqueConnection function getUniqueConnection the one that returns mock / stub. It seems to be bad to check the layout, but I see no way to achieve what I'm after. UniqueConnection is a third-party vendor library and cannot be changed.

+4
source share
5 answers

You can make a UniqueConnectionFactory and pass an instance of this file to FooClass. Then you have

  private function getUniqueConnection( $id ) { return $this->uniqueConnectionFactory->create( $id ); } 

In general, this is one of the advantages of using factory - you keep the new operator outside the class, which allows you to more easily modify the created object.

+4
source

While you cannot spend time reorganizing the code to use the factory, as the rambo encoder recommends, you can use a partial layout to return a non-destructive unique connection. When you find yourself in this position, this usually means that the test class has more than one responsibility.

 function testSomething() { $mockConn = $this->getMock('UniqueConnection'); $mockConn->expects($this->once()) ->method('doSomethingDestructive') ->will(...); $mockFoo = $this->getMock('FooClass', array('getUniqueConnection')); $mockFoo->expects($this->once()) ->method('getUniqueConnection') ->will($this->returnValue($mockConn)); $mockFoo->doSomething(); } 
+2
source

As Rambo Coder said, this is a question of too much in your class. I would not go as far as I would like to create a Factory, especially if you only create an instance of class one . The simplest solution would be to remove the responsibility for creating UniqueConnection:

 <?php class FooClass { public function doSomething( UniqueConnection $connection ) { $connection->doSomethingDestructive( ); } } 

Pass the layout when you are testing, pass new UniqueConnection( $user->id ) in real code ..

+2
source

Creating classes so that they can support various ways of execution is very important in some cases. One of these cases is what you are asking for.

Create classes to support various modes. for instance

 Class Connection { private $mode; public function setMode($mode) { $this -> $mode = $mode; } } 

Now your doSomethingDestructive can act according to the execution mode.

 public function doSomethingDestructive() { if($this -> mode === "test") { //if we are in a test mode scenario //Log something // Or just do some logging and give a message } else { // do what it was suppose to do } } 

The next time you test the class, you don’t have to worry about the destructive function doing something destruction by accident.

  public function doSomething( $user ) { $conn = $this->getUniqueConnection( $user->id ); $conn -> setMode("test"); //Now we are safe $conn->doSomethingDestructive(); //But the Testing is still being Ran } 
0
source

In this case, what you want is not a mock object, but a testing subclass. Separate $conn->doSomethingDestructive(); on the method, then subclass FooClass as TestFooClass and override the new method in the subclass. You can then test with a subclass without getting unwanted destructive behavior.

For instance:

 class FooClass { public function doSomething( $user ) { $conn = $this->getUniqueConnection( $user->id ); $this->connDoSomethingDestructive($conn); } protected function connDoSomethingDestructive($conn) { $conn->doSomethingDestructive(); } private function getUniqueConnection( $id ) { return new UniqueConnection( $id ); } } class TestFooClass extends FooClass { protected function connDoSomethingDestructive() { } private function getUniqueConnection( $id ) { return new MockUniqueConnection( $id ); } } 
0
source

All Articles