Unittests - Good Test Bad Test

- Aristotle

"Quality is not an act, it is a habit.​"

Fundamentals

Bad tests vs. good tests

Features of a good test

Agenda

Fundamentals

testpyramid

Why Unittests?

Finding regressions in future

Improve design

Documentation

Reducing cost of change

Good test

vs.

Bad test

multiple assertions

<?php
    public function testCalculation()
    {
        $testobj = new Calculator();

        $this->assertEquals(4, $testobj->add(2, 2));
        $this->assertEquals(10, $testobj->multiply(2, 5));
        $this->assertEquals(3, $testobj->diversify(9, 3));
        $this->assertEquals(3, $testobj->sub(7, 4));
    }
?>

After a fail following assertions won't execute

Use multiple tests instead!

Dependency usage

<?php
    public function testGetUsedBooks()
    {
        $db = new Database();
        $testobj = new Library();
        $testobj->load($db);

        $this->assertEquals([1, 3, 5], $testobj->getUsedBooks());
    }
?>

Real databases, file systems etc. will slow down and add complexity to our tests!

Use mocks instead!

# connect... select... foo...

unnecessary preconditions

<?php
    public function testGetNewBooks()
    {
        $dbMock = new DatabaseMock();
        $testobj = new Library();
        $testobj->setName('MyHappyLibrary');
        $testobj->load($dbMock);

        $this->assertEquals([2, 4], $testobj->getNewBooks());
    }

    public function testAddCalculation()
    {
        $dbMock = new DatabaseMock();
        $testobj = new Calculator();

        $this->assertEquals(4, $testobj->add(2, 2));
    }
?>

The code for the fixture is unnecessary or copy pasted!

Use short fixture code, the lesser the better!

# happy library, but no use...

runtime Problems

<?php
    public function testGetBorrowedBooksYesterday()
    {
        $dbMock = new DatabaseMock([ 5 => 1494833463 ]);
        $testobj = new Library();
        $testobj->load($dbMock);
        $date = new DateTime('now - 1 day');

        $this->assertEquals([5], $testobj->getBorrowedBooks($date));
    }
?>

The test will not pass on every day!

Avoid dynamic values!

<?php
    public function testGetBorrowedBooksYesterdayConditional()
    {
        $dbMock = new DatabaseMock([ 5 => 1494833463 ]);
        $testobj = new Library();
        $testobj->load($dbMock);
        $date = new DateTime('now - 1 day');

        if ($date->getTimestamp() == 1494833463 + 86000) {
            $expected = [5];
        } else {
            $expected = [];
        }

        $this->assertEquals($expected, $testobj->getBorrowedBooks($date));
    }
?>

# which timestamp is it on runtime?

# does it work today?

features of a good test

testing one code unit at a time

avoid conditions

use mocking

keep preconditions clean

single assertion

Copy of Unittests - Good Test bad Test

By Martin Jainta

Copy of Unittests - Good Test bad Test

  • 209