Testing is an important area of any application, when you write some code you need to make sure that all your logic is working correctly.
In this tutorial we're going to learn when and how you will mock a classes in your php tests.
When To Mock
Imagine we have a class to get information from a thrid party api, such as getting tweets for a specific user like below.
<?php
namespace Twitter;
use GuzzleHttp\Client;
class TwitterClient
{
/**
* @var Client
*/
private $client;
/**
* TwitterClient constructor.
* @param Client $client
*/
public function __construct(Client $client)
{
$this->client = $client;
}
/**
* @return \Psr\Http\Message\ResponseInterface
*/
public function getTweets()
{
return $this->client->get('https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=paulund');
}
}
As you can see in this class it's dependency is the Guzzle client, we use this to send a GET
request to the twitter
API. The information we get back from this API can change over time, therefore if we have a test that depends on the
same information then the test could fail over time.
Now if we have a class to get the latest tweets.
<?php
namespace Twitter;
class LatestTweets
{
/**
* @var TwitterClient
*/
private $twitterClient;
/**
* LatestTweets constructor.
* @param TwitterClient $twitterClient
*/
public function __construct(TwitterClient $twitterClient)
{
$this->twitterClient = $twitterClient;
}
public function tweets()
{
return collect($this->twitterClient->getTweets())->take(5);
}
}
As we're making sure we only get 5 tweets back from the API we need to test what happens if we return more than 5 tweets and less than 5 tweets.
By using mocking we can make sure that we define in our test exactly what comes back from the API to test specific responses.
How To Mock
To mock a class in PHP you can use the library Mockery.
The easiest way is to use mockery by using the static method and pass in the class you want to mock.
$twitterMock = \Mockery::mock(TwitterClient::class);
We can now use this mock object to change the response of the methods.
In our class LatestTweets
we use the method getTweets
to get the tweets for the profile. With mockery we can define
what we get back from each method by using shouldReceive
and andReturn
.
$twitterMock->shouldReceive('getTweets')->andReturn([
[
'text' => $faker->paragraph
],
[
'text' => $faker->paragraph
],
[
'text' => $faker->paragraph
]
]);
This makes sure we can guarantee what we get back from the API.
<?php
namespace Tests;
use Faker\Generator;
use Twitter\LatestTweets;
use Twitter\TwitterClient;
class LatestTweetsTest extends TestCase
{
/** @test */
public function it_returns_tweets_from_twitter_client()
{
// Given
$faker = app(Generator::class);
$twitterMock = \Mockery::mock(TwitterClient::class);
$twitterMock->shouldReceive('getTweets')->andReturn([
[
'text' => $faker->paragraph
],
[
'text' => $faker->paragraph
],
[
'text' => $faker->paragraph
]
]);
// When
$latestTweets = new LatestTweets($twitterMock);
// Then
$this->assertCount(3, $latestTweets->tweets());
}
/** @test */
public function it_only_returns_5_tweets_from_twitter_client()
{
// Given
$faker = app(Generator::class);
$twitterMock = \Mockery::mock(TwitterClient::class);
$twitterMock->shouldReceive('getTweets')->andReturn([
[
'text' => $faker->paragraph
],
[
'text' => $faker->paragraph
],
[
'text' => $faker->paragraph
],
[
'text' => $faker->paragraph
],
[
'text' => $faker->paragraph
],
[
'text' => $faker->paragraph
]
]);
// When
$latestTweets = new LatestTweets($twitterMock);
// Then
$this->assertCount(5, $latestTweets->tweets());
}
}