I don't have time to write tests because I am too busy
Multiple runs of the test should consistently return true or consistently return false, provided no changes
Only two possible results : PASS or FAIL
No partially successful tests
No side effects between tests
Isolation of tests
One test should be responsible for one scenario only
Test behavior, not methods
Unit test must be easy to read and understand
Name tests to represent PASS conditions:
<?php
namespace Riskio\SpecificationTest;
use Riskio\Specification\AndSpecification;
use Riskio\SpecificationTest\Fixtures\NotSatisfiedSpecification;
use Riskio\SpecificationTest\Fixtures\SatisfiedSpecification;
class AndSpecificationTest extends \PHPUnit_Framework_TestCase
{
/**
* @test
*/
public function isSatisfiedBy_GivenBothSpecificationsSatisfied_ShouldReturnTrue()
{
$spec = new AndSpecification(
new SatisfiedSpecification(),
new SatisfiedSpecification()
);
$isSatisfied = $spec->isSatisfiedBy('foo');
$this->assertThat($isSatisfied, $this->equalTo(true));
}
/**
* @test
*/
public function isSatisfiedBy_GivenOneSpecificationNotSatisfied_ShouldReturnFalse()
{
$spec = new AndSpecification(
new SatisfiedSpecification(),
new NotSatisfiedSpecification()
);
$isSatisfied = $spec->isSatisfiedBy('foo');
$this->assertThat($isSatisfied, $this->equalTo(false));
}
/**
* @test
*/
public function isSatisfiedBy_GivenBothSpecificationsNotSatisfied_ShouldReturnFalse()
{
$spec = new AndSpecification(
new NotSatisfiedSpecification(),
new NotSatisfiedSpecification()
);
$isSatisfied = $spec->isSatisfiedBy('foo');
$this->assertThat($isSatisfied, $this->equalTo(false));
}
}
Test should have no uncertainty:
Test should not contain "while", "for" or "foreach" loops
Indicate expected exception
Catch only the expected type of exception
Fail test if expected exception is not caught
Let other exceptions go uncaught
<?php
use PHPUnit\Framework\TestCase;
class MyTest extends TestCase
{
/**
* @expectedException MyException
* @expectedExceptionCode 20
*/
public function testExceptionHasErrorCode20()
{
throw new MyException('Foo', 20);
}
/**
* @expectedException MyException
* @expectedExceptionMessage Foo
*/
public function testExceptionHasErrorMessageFoo()
{
throw new MyException('foo', 20);
}
public function testExceptionHasErrorMessageBar()
{
$this->expectException(MyException::class);
throw new MyException('foo', 20);
}
}
By reading the assertion message, one should know why the test failed and what to do
Include business logic information in the assertion message
Good assertion messages:
Assertions are the "checks" that you may perform to determine if a test passes or fails.
As possible, you use only one assertion per test
Use the more relevant assertion in each case.
assertEquals(true, $val)
assertTrue($val)
Use the more relevant assertion in each case.
assertEquals(null, $val)
assertNull($val)
Use the more relevant assertion in each case.
assertEquals(true, array_key_exists($items, $key))
assertArrayHasKey($key, $items)
A mock allows a dependency to be imitated so the Unit test can be isolated
you can't go into production with less than 87% coverage
Metric uses to determine code covered by unit tests
Useful to find untested code
Do not guarantee test quality and use cases coverage
We can have 100% code coverage without any assertions
Behavioral Driven Development Oriented
Describing the software behavior
TDD focus on testing your application, BDD is more about describing its behavior
Using a BDD approach will force you to constantly consider the actual requirements and desired behavior of the software you're building.
No code coverage