Skip to content

Instantly share code, notes, and snippets.

@hofmannsven
Last active September 17, 2023 20:13
Show Gist options
  • Save hofmannsven/67deb0d36c991246b0a0 to your computer and use it in GitHub Desktop.
Save hofmannsven/67deb0d36c991246b0a0 to your computer and use it in GitHub Desktop.
Testing with PHPUnit

Testing with PHPUnit

Improved Error Handling and Styling

Workflow

  1. Given
  2. When
  3. Then

Usage

  • Run tests: phpunit aka vendor/bin/phpunit
  • Version: phpunit --version
  • Run testsuite only: phpunit --testsuite testsuitename
  • Run method only: phpunit --filter myTestMethod
  • Run group only: phpunit --group groupname
  • Generate coverage report: phpunit --coverage-html tests/coverage

PHPUnit & PhpStorm

External lib. include path (OS X & Mamp): /Applications/MAMP/Library/bin

PHPUnit & Laravel

PHPUnit & VVV

  • Defined version of PHPUnit: /usr/local/src/composer/composer.json
  • Update: sudo composer update

PHPUnit & WordPress

PHPUnit & WP-CLI


Notes

  • Every test-method has to be public and a method name starting with test or have the @test notation
  • Mock: Does not trigger the actual class, calls the interface instead (set expectation on what should happen)
  • Spy: Triggers the actual class, verify that it happened afterwards (reverse mock: do the action, verify that it happened)
  • Stubs: Returns any kind of hard-coded data (pre-defined values)
  • Dummies: Adheres to the contract just to run the test (methods will return NULL)
  • Factory: reusable element for test-related setup (for creating other objects)
  • Fixture: also a reusable element for test-related setup
  • A test-related method (called fixture) can be a private method not starting with test
  • Method visibility can be switched from protected to public via a proxy class
  • Skip tests via $this->markTestSkipped( 'Must be revisited.' )
  • Coverage report requires Xdebug extension
  • Tests coverage can be documented with @covers notation

Laravel Notes

  • Disable exception handling for a test: $this->withoutExceptionHandling()
  • Act as signed-in user: $this->actingAs(factory('App\User')->create());

Laravel Mockery Testing Helpers

Given App\Project class:

class Project {
	public function subscribeTo($name, $user) {
		//
	}
}

Mocking the object

// Create user
$user = factory(User::class)->create();

// Create mock
$this->mock(Project::class, function ($mock) use ($user) {
	$mock->shouldReceive()->subscribeTo('members', $user)->once();
});

// Perform action
$this->actingAs($user)->get('/endpoint');

Spying the object

// Create user
$user = factory(User::class)->create();

// Create spy
$spy = $this->spy(Project::class);

// Perform action
$this->actingAs($user)->get('/endpoint');

// Confirm action
$spy->shouldHaveReceived()->subscribeTo('members', $user);
# PHPUnit completion
source ~/phpunit.bash
# Bash-Completion script for PHPUnit
#
# Created by Henrique Moody <henriquemoody@gmail.com>
# https://gist.github.com/henriquemoody/5014805
#
_phpunit()
{
COMPREPLY=()
local cur="${COMP_WORDS[COMP_CWORD]}"
local prev="${COMP_WORDS[COMP_CWORD-1]}"
local opts="--coverage-clover --coverage-crap4j --coverage-html --coverage-php --coverage-text --coverage-xml --log-junit --log-tap --log-json --testdox-html --testdox-text --filter --testsuite --group --exclude-group --list-groups --test-suffix --report-useless-tests --strict-coverage --disallow-test-output --enforce-time-limit --disallow-todo-tests --process-isolation --no-globals-backup --static-backup --colors --columns --columns --stderr --stop-on-error --stop-on-failure --stop-on-risky --stop-on-skipped --stop-on-incomplete -v --verbose --debug --loader --repeat --tap --testdox --printer --bootstrap -c --configuration --no-configuration --include-path -d -h --help --version"
local diropts="--coverage-html|--coverage-xml|--include-path"
local nocompleteopts="--filter|--testsuite|--group|--exclude-group|--test-suffix|--loader|--repeat|--printer|-d|-h|--help|--version"
if [[ ${prev} =~ ${diropts} ]]; then
COMPREPLY=( $(compgen -d -- ${cur}) )
return 0
elif [[ ${prev} =~ ${nocompleteopts} ]]; then
return 0
elif [[ ${prev} = --columns ]]; then
COMPREPLY=( $(compgen -W "max" -- ${cur}) )
return 0
elif [[ ${prev} = --colors ]]; then
COMPREPLY=( $(compgen -W "auto never always" -- ${cur}) )
return 0
elif [[ "${cur}" == -* ]]; then
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
return 0
else
COMPREPLY=( $(compgen -f -- "${cur}") )
return 0
fi
}
complete -F _phpunit phpunit
<?xml version="1.0" encoding="UTF-8"?>
<phpunit
bootstrap="./tests/bootstrap.php"
backupGlobals="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
>
<php>
<const name="PluginNamespace\Tests\DB_NAME" value="PLACEHOLDER"/>
<const name="PluginNamespace\Tests\DB_USER" value="PLACEHOLDER"/>
<const name="PluginNamespace\Tests\DB_PASSWORD" value="PLACEHOLDER"/>
<const name="PluginNamespace\Tests\DB_HOST" value="localhost"/>
<const name="PluginNamespace\Tests\DB_CHARSET" value="utf8"/>
<const name="PluginNamespace\Tests\DB_COLLATE" value=""/>
<const name="PluginNamespace\Tests\DB_TABLE_PREFIX" value="wp_"/>
</php>
<testsuites>
<testsuite name="Integration">
<directory suffix="Test.php" phpVersion="5.6.0" phpVersionOperator=">=">./tests/Integration</directory>
</testsuite>
</testsuites>
<logging>
<log type="coverage-clover" target="build/logs/clover.xml"/>
</logging>
<filter>
<whitelist>
<directory>./src</directory>
<exclude>
<directory>./assets</directory>
<directory>./languages</directory>
<directory>./resources</directory>
<directory>./tests</directory>
<directory>./vendor</directory>
</exclude>
</whitelist>
</filter>
</phpunit>
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
bootstrap="vendor/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
printerClass="Codedungeon\PHPUnitPrettyResultPrinter\Printer">
<testsuites>
<testsuite name="Unit">
<directory suffix="Test.php">./tests/Unit</directory>
</testsuite>
<testsuite name="Feature">
<directory suffix="Test.php">./tests/Feature</directory>
</testsuite>
</testsuites>
<listeners>
<listener class="NunoMaduro\Collision\Adapters\Phpunit\Listener" />
</listeners>
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">./app</directory>
</whitelist>
</filter>
<php>
<server name="APP_ENV" value="testing"/>
<server name="BCRYPT_ROUNDS" value="4"/>
<server name="CACHE_DRIVER" value="array"/>
<server name="MAIL_DRIVER" value="array"/>
<server name="QUEUE_CONNECTION" value="sync"/>
<server name="SESSION_DRIVER" value="array"/>
<server name="DB_CONNECTION" value="sqlite"/>
<server name="DB_DATABASE" value=":memory:"/>
</php>
</phpunit>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment