DEV Community

Cover image for Module 2: Writing Basic Tests and Assertions
Antonio Silva
Antonio Silva

Posted on

Module 2: Writing Basic Tests and Assertions

Now that you have your environment set up and have run your first test, it's time to deepen your knowledge of writing tests with PHPUnit. In this module, we will explore the structure of a test in more detail and dive into the world of assertions, which are the heart of any test.

1. Structure of a Test

As we saw in the previous module, every test in PHPUnit is a class that extends PHPUnit\Framework\TestCase. Let's take a closer look at the structure:

<?php

// tests/MathTest.php

use PHPUnit\Framework\TestCase;

class MathTest extends TestCase
{
    public function testSumShouldReturnTheCorrectValue()
    {
        // ... test body ...
    }

    public function testSubtractionShouldWorkCorrectly()
    {
        // ... test body ...
    }
}
Enter fullscreen mode Exit fullscreen mode

Important Conventions:

  • Class Name: The test class usually has the same name as the class being tested, with the suffix Test. For example, to test a Math class, the test class would be MathTest.
  • Method Name: The name of the test method must begin with the prefix test. A good practice is to make the method name descriptive, indicating what is being tested and what the expected result is. For example, instead of testSum(), use testSumShouldReturnTheCorrectValue(). This makes your tests more readable, and when a test fails, the method name already gives you a clue as to what went wrong.

2. The Power of Assertions

Assertions are the methods you use to verify that the result of your code is what you expect. If an assertion fails, PHPUnit stops the execution of that test and marks it as "failed".

Let's get to know the most common and useful assertions:

a) Checking for Equality:

  • assertEquals($expected, $actual): Checks if two variables are equal in value. This is by far the most used assertion.

    public function testSumShouldReturnTheCorrectValue()
    {
        $result = 2 + 3;
        $this->assertEquals(5, $result);
    }
    
  • assertSame($expected, $actual): Checks if two variables are identical, meaning they have the same type and the same value.

    public function testVariableTypes()
    {
        $this->assertEquals(5, "5"); // Passes, because the values are equal
        $this->assertSame(5, "5");   // Fails, because the types are different (integer vs. string)
    }
    

b) Checking Boolean Conditions:

  • assertTrue($condition): Checks if a condition is true.

  • assertFalse($condition): Checks if a condition is false.

    public function testIsEvenNumber()
    {
        $number = 4;
        $isEven = ($number % 2 == 0);
        $this->assertTrue($isEven);
    }
    

c) Checking for Nulls:

  • assertNull($variable): Checks if a variable is null.

  • assertNotNull($variable): Checks if a variable is not null.

d) Checking Counts and Arrays:

  • assertCount($expectedCount, $array): Checks if an array has a specific number of elements.

    public function testArrayHasThreeItems()
    {
        $items = ['apple', 'banana', 'orange'];
        $this->assertCount(3, $items);
    }
    
  • assertArrayHasKey($key, $array): Checks if an array contains a given key.

  • assertContains($value, $array): Checks if an array contains a given value.

  • assertEmpty($variable): Checks if a variable is empty (useful for strings, arrays, etc.).

e) Checking Object Types:

  • assertInstanceOf($expectedClass, $object): Checks if an object is an instance of a given class.

    // Assuming you have a 'User' class
    public function testUserCreation()
    {
        $user = new User();
        $this->assertInstanceOf(User::class, $user);
    }
    

f) Optional: Adding Custom Error Messages

All assertions accept an optional last argument: a custom error message. If the test fails, this message will be displayed, which can help you understand the problem more quickly.

public function testSumWithErrorMessage()
{
    $result = 2 + 2;
    $this->assertEquals(5, $result, 'The sum of 2 + 2 should be 5, but it was not.');
}
Enter fullscreen mode Exit fullscreen mode

3. Testing Exceptions

Sometimes, the expected behavior of your code is to throw an exception. For example, when trying to divide a number by zero. PHPUnit offers an elegant way to test this:

// In a 'Calculator' class
public function divide($a, $b)
{
    if ($b == 0) {
        throw new InvalidArgumentException('Cannot divide by zero.');
    }
    return $a / $b;
}

// In your test
public function testDivisionByZeroThrowsException()
{
    $this->expectException(InvalidArgumentException::class);
    $this->expectExceptionMessage('Cannot divide by zero.');

    $calculator = new Calculator();
    $calculator->divide(10, 0);
}
Enter fullscreen mode Exit fullscreen mode
  • expectException(): Tells PHPUnit that you expect an exception of a certain type to be thrown.

  • expectExceptionMessage(): (Optional) Checks if the exception message is what you expect.

If the expected exception is thrown, the test passes. If no exception is thrown, or if an exception of a different type is thrown, the test fails.

Next Steps:

With this knowledge, you can now write a variety of unit tests for your code. In the next module, we will explore how to organize your tests more effectively, using Data Providers to avoid code repetition and understanding the test lifecycle with the setUp() and tearDown() methods.

Top comments (0)