I think the problem you are having is that you are missing a setup that ensures the right test outcome. That may sound a bit odd, but consider this scenario
class ConnectedResource
{
private $connector;
public function __construct($connector)
{
$this->connector = $connector;
}
public function connectToDevice()
{
try {
$this->connector->connect();
} catch (\Exception $e) {
return false;
}
return true;
}
}
That is really simplified, but the essential bit is that you want to test both paths: connect() success and the Exception.
What you would do in your test now is either
public function testSuccessfulConnectReturnsTrue()
{
$connector = new Connector(
// config to make a successful connection
);
$myObject = new ConnectedResource($connector);
$this->assertTrue($myObject->connectToDevice());
}
public function testFailedConnectReturnsFalse()
{
$connector = new Connector(
// invalid config that will raise an exception
);
$myObject = new ConnectedResource($connector);
$this->assertFalse($myObject->connectToDevice());
}
Now both tests will work, because they use different connections (one that works and one that doesn't).
The other possibility is to pass in a Mock-Connector. In other words instead of a real connector you create a dummy object where you can decide yourself what $this->connector->connect() would return. Think of it as something along the lines of "assuming I have a connector that returns true then ConnectedResource should behave like this". The code for this could look something like this:
public function testSuccessfulConnectReturnsTrue()
{
$connector = $this->createMock(Connector::class);
$connector->expect($this->any())
->method('connect')
->willReturn(true)
;
$myObject = new ConnectedResource($connector);
$this->assertTrue($myObject->connectToDevice());
}
and for the failure scenario it would look something like this:
public function testFailedConnectReturnsFalse()
{
$connector = $this->createMock(Connector::class);
$connector->expect($this->any())
->method('connect')
->willReturn($this->throwException(new Exception))
;
$myObject = new ConnectedResource($connector);
$this->assertTrue($myObject->connectToDevice());
}
Now you control everything outside the scope of the test (the connection) and only test the behavior defined inside connectToDevice(). This will make your test safe against changes in the other class, but could cause problems when for example in a future update the connect() function changes, e.g. arguments are added or changed.
The important bit is, that you have to ensure the requirements for runnning your test are fulfilled.
connectToDevice) it should return/do what I defined, then its test succeeds. So the test it's not strictly correlated to a function success rather to a return or an action that happens when a function is called. As I said, I'm at beginning with unit tests, so I don't know if my argumentation is right.connectToDevicein two test functions without changing the environment. This two functions are opposite => you will always have a test that fails. To change that you have in one of the functions to manipulate the environment and then you will have always the test to be successful.