Reputation: 1018
I have created one REST API, which does CRUD operation on DB and returns the result from DB. Now, this is my below code:
class CreateRecords
{
public function createEntities($details)
{
if (trim($details['username']) == "") {
$this->result = "Username is empty.";
} else {
$this->result = create_record($userDetails);
}
return $this->result;
}
}
Here, in this above code, create_record
is a function which does validation part and insert the details into db and returns the id of the newly created record.
Now, currently my test case is like this:
class TestCreateRecords
{
public function setUp()
{
$this->client = new GuzzleHttp\Client([
'base_uri' => 'http://localhost/test/'
]);
}
public function testCreateEntitiesAPI()
{
$response = $this->client->post('service.php', [
'json' => [
'username' => '[email protected]',
'user_password' => 'test',
'first_name' => 'Test',
'last_name' => 'test',
'email1' => '[email protected]'
]
]);
$actual_response = json_decode($response->getBody(), true);
$this->assertArrayHasKey("id", $actual_response, 'Un-successful.');
}
}
So, I have few queries regarding this:
In my main class file, create_record
function validates the username
before inserting details into DB. It checks if the username
is already available or not. If available, it won't create a record and sends response as Already Available
. If not available, then it will create a record and sends newly created unique id.
Now, I want to test both the scenarios, where passed username
is already available in DB and not available in DB. Now, with my above test case, I'll have to make changes in it every time before running the test, so I can test the scenario, where username
is already not available in DB. And it seems illogical to me.
I would like to know, is there any other way I can accomplish this?
Someone advised me to mock the create_record
method to test the API and write another test case to test the create_record
method itself. But as per my application MVC framework, it seems impossible for me, right now. Because in create_record
method, there are dozens of methods which are being called and these methods calls few others methods. And again, I'll have to mock those dozens methods and write the test cases for them and so on.
It is time and effort consuming for us. So I have decided to not to mock any methods and test the whole API flow in one test case.
I would like to know, is it a good approach? And what will be the side-effects of it?
Upvotes: 0
Views: 842
Reputation: 3483
Whoever advised you to try mocking is right. It's great that you want to start testing more, but it often turns up problems in application architecture.
Unit tests should ideally test just a single class or function. If all of your functions are calling other functions all the way down to the database insertion it's an end-to-end test. It's fine to have these, but in your case you want to test a variety of cases (when validation fails because username exists) and you'll soon find that testing all your edge cases in end-to-end tests is slow.
You already have a CreateRecords
class. Where is this used in the service.php
class? If you're using some sort of dependency injection you can mock the CreateRecords
class in the controller (which I'm guessing is service.php
for you. In this way you can test the service separately from the API code. Here's an example:
/**
* Controller action
*/
function myAction() {
if ($this->isAdmin()) {
throw new \Exception('You need to be an admin');
}
$this->userService->create($_POST);
}
/**
* Service containing actions related to users
*/
class UserService {
/**
* @var UserValidator
*/
protected $validator;
/**
* @var RecordSaver
*/
protected $recordSaver;
/**
* @param UserValidator $validator
* @param RecordSaver $recordSaver
*/
public function __construct(
UserValidator $validator,
RecordSaver $recordSaver
) {
$this->validator = $validator;
$this->recordSaver = $recordSaver;
}
/**
* @param $params
* @return array
*/
public function create($params) {
$this->validator->validate($params);
return $this->recordSaver->save($params);
}
}
/**
* Validation of new users / or updating existing
*/
class UserValidator {
/**
* @param array $params
* @throws Exception
*/
public function validate($params) {
if (!isset($params['username'])) {
throw new \Exception('Username must be set');
}
}
}
/**
* Wrapper for database calls
*/
class RecordSaver {
/**
* @param array $params
* @return array
*/
public function save($params) {
return create_record($params);
}
}
Then you can create tests that test each layer only, e.g.
Upvotes: 1