patternphpMinor
My first model test in PHPUnit
Viewed 0 times
firstmodeltestphpunit
Problem
I've just created a test to create my table gateway class. I've written about 8 tests and all are passing. I'm hoping anyone can offer any advice on what to do next to make these better. This is quite a small app and just one of my own, but at work I want to write tests for much bigger applications so concerned with performance and writing tests faster.
I'm querying a test database for this. As you can see I clean the user table on every test.
```
$GLOBALS['DB_DSN'],
'user' => $GLOBALS['DB_USER'],
'password' => $GLOBALS['DB_PASSWD'],
));
$this->userTable = new UserTable($dbAdapter);
// clean table on every test so we start clean
$this->userTable->delete(array('id <> 999999999'));
}
public function testCreateUser()
{
// get default values for new user
$values = $this->getUserValues();
// count number of records before create
$before = count($this->userTable->fetchAll());
// assert user is created
$result = $this->userTable->create($values);
$this->assertTrue($result);
// check users has been incremented
$after = count($this->userTable->fetchAll());
$this->assertGreaterThan($before, $after);
}
/**
* @depends testCreateUser
*/
public function testCreateUserDoesntInsertDuplicateEmail()
{
// new user to be created
$values = $this->getUserValues();
// assert user is created
$result = $this->userTable->create($values);
$this->assertTrue($result);
// try again with same email (should be false)
$result = $this->userTable->create($values);
$this->assertFalse($result);
}
/**
* @depends testCreateUser
*/
public function testFetchUserByCriteria()
{
// create new user
$values = $this->getUserValues();
$user = $this->createAndFetchUser($values);
$this->assertEquals($user['email'],
I'm querying a test database for this. As you can see I clean the user table on every test.
```
$GLOBALS['DB_DSN'],
'user' => $GLOBALS['DB_USER'],
'password' => $GLOBALS['DB_PASSWD'],
));
$this->userTable = new UserTable($dbAdapter);
// clean table on every test so we start clean
$this->userTable->delete(array('id <> 999999999'));
}
public function testCreateUser()
{
// get default values for new user
$values = $this->getUserValues();
// count number of records before create
$before = count($this->userTable->fetchAll());
// assert user is created
$result = $this->userTable->create($values);
$this->assertTrue($result);
// check users has been incremented
$after = count($this->userTable->fetchAll());
$this->assertGreaterThan($before, $after);
}
/**
* @depends testCreateUser
*/
public function testCreateUserDoesntInsertDuplicateEmail()
{
// new user to be created
$values = $this->getUserValues();
// assert user is created
$result = $this->userTable->create($values);
$this->assertTrue($result);
// try again with same email (should be false)
$result = $this->userTable->create($values);
$this->assertFalse($result);
}
/**
* @depends testCreateUser
*/
public function testFetchUserByCriteria()
{
// create new user
$values = $this->getUserValues();
$user = $this->createAndFetchUser($values);
$this->assertEquals($user['email'],
Solution
Slow vs. fast tests
This test is testing not only your table gateway class, but also your database. Thus it is going to be slow because it has to prepare the test environment before each test and clean up afterwards. This kind of test is still good to have, it would catch integration errors between your gateway class and your database environment. I call them integration tests to distinguish them from the faster unit tests. The definition of a unit test by Michael Feathers helps drawing the line.
A test is not a unit test if:
Tests that do these things aren't bad. Often they are worth writing, and they can be written in a unit test harness. However, it is important to be able to separate them from true unit tests so that we can keep a set of tests that we can run fast whenever we make our changes.
The point here is to split execution of your tests into two or more stages: first run unit tests, then if they all pass run the integration tests, then if they all pass run the other tests (system, acceptance, etc.) and so on. This way you preserve a short feedback loop to quickly notice failed tests. Moreover you are not bored by having to run long tests all the time (which leads to people not running the tests at all).
In your case, I would suggest you first test the table gateway class in isolation by passing it a mock database adapter. Then use this mock object to assert that the gateway generates the correct queries. This will run much faster than anything connected to a database. For example, you can use PHPUnit to run your unit tests, then use Codeception (thanks to @EliasVanOotegem for the suggestion) to run integration tests, thus splitting into two test runs.
This test is testing not only your table gateway class, but also your database. Thus it is going to be slow because it has to prepare the test environment before each test and clean up afterwards. This kind of test is still good to have, it would catch integration errors between your gateway class and your database environment. I call them integration tests to distinguish them from the faster unit tests. The definition of a unit test by Michael Feathers helps drawing the line.
A test is not a unit test if:
- It talks to the database
- It communicates across the network
- It touches the file system
- It can't run at the same time as any of your other unit tests
- You have to do special things to your environment (such as editing config files) to run it.
Tests that do these things aren't bad. Often they are worth writing, and they can be written in a unit test harness. However, it is important to be able to separate them from true unit tests so that we can keep a set of tests that we can run fast whenever we make our changes.
The point here is to split execution of your tests into two or more stages: first run unit tests, then if they all pass run the integration tests, then if they all pass run the other tests (system, acceptance, etc.) and so on. This way you preserve a short feedback loop to quickly notice failed tests. Moreover you are not bored by having to run long tests all the time (which leads to people not running the tests at all).
In your case, I would suggest you first test the table gateway class in isolation by passing it a mock database adapter. Then use this mock object to assert that the gateway generates the correct queries. This will run much faster than anything connected to a database. For example, you can use PHPUnit to run your unit tests, then use Codeception (thanks to @EliasVanOotegem for the suggestion) to run integration tests, thus splitting into two test runs.
Context
StackExchange Code Review Q#59662, answer score: 3
Revisions (0)
No revisions yet.