PHPUnit provides methods that are used to automatically create objects that will replace the original object in our test. createMock($type) and getMockBuilder($type) methods are used to create mock object. The createMock method immediately returns a mock object of the specified type.
PHPUnit is a unit testing framework for the PHP programming language. It is an instance of the xUnit design for unit testing systems that began with SUnit and became popular with JUnit. Even a small software development project usually takes hours of hard work.
Two approaches that come to mind:
Create two adapters for your FTP class:
A "mock" one that does not actually connect to anything and only returns seeded data.
The FTP class' connect()
method then looks like this:
public function connect($name, $opt=array())
{
return $this->getAdapter()->connect($name, $opt);
}
The mock adapter might look something like this:
class FTPMockAdapter
implements IFTPAdapter
{
protected $_seeded = array();
public function connect($name, $opt=array())
{
return $this->_seeded['connect'][serialize(compact('name', 'opt'))];
}
public function seed($data, $method, $opt)
{
$this->_seeded[$method][serialize($opt)] = $data;
}
}
In your test, you would then seed the adapter with a result and verify that connect()
is getting called appropriately:
public function setUp( )
{
$this->_adapter = new FTPMockAdapter();
$this->_fixture->setAdapter($this->_adapter);
}
/** This test is worthless for testing the FTP class, as it
* basically only tests the mock adapter, but hopefully
* it at least illustrates the idea at work.
*/
public function testConnect( )
{
$name = '...';
$opt = array(...);
$success = true
// Seed the connection response to the adapter.
$this->_adapter->seed($success, 'connect', compact('name', 'opt'));
// Invoke the fixture's connect() method and make sure it invokes the
// adapter properly.
$this->assertEquals($success, $this->_fixture->connect($name, $opt),
'Expected connect() to connect to correct server.'
);
}
In the above test case, setUp()
injects the mock adapter so that tests can invoke the FTP class' connect()
method without actually triggering an FTP connection. The test then seeds the adapter with a result that will only be returned if the adapter's connect()
method were called with the correct parameters.
The advantage to this method is that you can customize the behavior of the mock object, and it keeps all of the code in one place if you need to use the mock across several test cases.
The disadvantages to this method are that you have to duplicate a lot of functionality that has already been built (see approach #2), and arguably you've now introduced another class that you have to write tests for.
An alternative is to use PHPUnit's mocking framework to create dynamic mock objects in your test. You'll still need to inject an adapter, but you can create it on-the-fly:
public function setUp( )
{
$this->_adapter = $this->getMock('FTPAdapter');
$this->_fixture->setAdapter($this->_adapter);
}
public function testConnect( )
{
$name = '...';
$opt = array(...);
$this->_adapter
->expects($this->once())
->method('connect')
->with($this->equalTo($name), $this->equalTo($opt))
->will($this->returnValue(true));
$this->assertTrue($this->_fixture->connect($name, $opt),
'Expected connect() to connect to correct server.'
);
}
Note that the above test mocks the adapter for the FTP class, not the FTP class itself, as that would be a rather silly thing to do.
This approach has advantages over the previous approach:
There are some disadvantages to this approach, however:
See http://phpunit.de/manual/current/en/test-doubles.html#test-doubles.mock-objects for more information.
Your approach seems to be O-K. There are always limitations to what you can unit test considering eventually you will be at low level functions that interact directly with externals.
I would recommend you base your FTP on adapters that you can mock out, and then you can cover the actual adapter testing via integration testing.
While one option would be to mock the FTP server and connect to it in tests (you would only need to change the ftp-server details/connection configuration to the mock server and not change any code) there is another one: You don't need to unit-test PHP's functions.
Those functions are not components written by you, so you should not test them.
Otherwise you could start to write test for if
or even operators like +
- but you don't.
To say more, it would be good to see your code. If you've got procedural style code, it's hard to mock/stub/dupe each FTP function. Actually this is not easily possible with PHP anyway.
But if you instead create a FTP connection object that wraps all these functions, you can create a test dupe for that FTP connection object. This requires refactoring of your code however.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With