Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it bad to mock the object being tested in a unit test?

Here is the class I am unit testing. Currently I am 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 gets a new instance of UniqueConnection (a class I am not testing here) based on a property of the argument it receives. The problem is that UniqueConnection:: doSomethingDestructive method is something I cannot call during tests due to its... destructiveness. So I would like to stub/mock the UniqueConnection rather than use a real one.

I don't see any way to inject my mocked UniqueConnection. I would make the UniqueConnection a constructor argument for FooClass but, as you can see, a new one gets created based on the parameter to the doSomething function and all the unique ids it may be called with are not known ahead of time.

My only option that I can see is to test a mock of FooClass instead of FooClass itself. Then I would replace the getUniqueConnection function with one that returns a mock/stub. This seems bad to test an mock, but I don't see any way to achieve what I am after otherwise. UniqueConnection is a third party vendor library and cannot be modified.

like image 946
william Avatar asked Feb 19 '23 12:02

william


2 Answers

You could make a UniqueConnectionFactory, and pass an instance of that to FooClass. Then you have

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

In general, this is one of the benefits of using a factory - you keep the new operator out of the class, which allows you to more easily vary the object being created.

like image 91
goat Avatar answered Mar 05 '23 01:03

goat


Like Rambo Coder said, it's a matter of doing too much in your class. I wouldn't go as far as wanting to create a Factory, especially if you'll only ever create an instance of one specific class. The simplest solution would be to invert the responsibility of creating the UniqueConnection:

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

Pass a mock when you're testing, pass a new UniqueConnection( $user->id ) in the real code..

like image 25
Berry Langerak Avatar answered Mar 05 '23 01:03

Berry Langerak