Is it bad to mock the object being tested in a unit test?
Question by user1566921
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.
Answer by Starx
Creating classes in a way that it can support different modes of execution is very important in some cases. One of these cases is what you are asking for.
Create your classes to support various modes. For example
Class Connection {
private $mode;
public function setMode($mode) {
$this -> $mode = $mode;
}
}
Now, your doSomethingDestructive
can act as per 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
}
}
Next time, when you are testing the class, you dont have to worry about that destructive function doing something destruction accidentally.
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
}