danielstjules / pho
行为驱动测试框架
Requires
- php: >=5.4.0
This package is not auto-updated.
Last update: 2024-09-14 15:32:18 UTC
README
PHP的行为驱动测试框架,受Jasmine和RSpec启发。它具有熟悉的语法,并具有监视命令,在开发期间自动重新运行规格。它还可以通过自定义匹配器和报告器进行扩展。
安装
以下说明概述了使用Composer进行安装。如果您没有Composer,您可以从中下载https://getcomposer.org.cn/ 如果您是composer的新手,请确保将供应商bin添加到您的PATH
# Append the following to your profile file, for example in ~/.profile export PATH=$HOME/.composer/vendor/bin:$PATH
要安装pho,请运行
composer global require danielstjules/pho
用法
Usage: pho [options] [files] Options -a --ascii Show ASCII art on completion -b --bootstrap <bootstrap> Bootstrap file to load -f --filter <pattern> Run specs containing a pattern -h --help Output usage information -n --namespace Only use namespaced functions -r --reporter <name> Specify the reporter to use -s --stop Stop on failure -v --version Display version number -w --watch Watch files for changes and rerun specs -C --no-color Disable terminal colors
编写规格
Pho公开了一个用于组织和编写测试的DSL,包括以下函数:describe
、context
、it
、before
、after
、beforeEach
、afterEach
和expect
。还有通过xdescribe
、xcontext
和xit
禁用规格和套件的等效函数。
要创建一个套件,可以使用字符串和函数传递给它们describe
和context
。两者可以互换使用,尽管context通常嵌套在describe中以分组一组行为。it
用于创建规格或测试。
一个规格可以包含多个期望或断言,只要所有断言都通过且没有未捕获的异常,它就会通过。在pho中,可以使用expect
来断言值。该函数接受要测试的值,并可以与一些匹配器链式使用。
<?php describe('A suite', function() { it('contains specs with expectations', function() { expect(true)->toBe(true); }); it('can have specs that fail', function() { expect(false)->not()->toBe(false); }); it('can have incomplete specs'); });
可以使用PHP的use
关键字在套件和规格之间传递对象。以下是一个示例
describe('Example', function() { $object = new stdClass(); $object->name = 'pho'; context('name', function() use ($object) { it('is set to pho', function() use ($object) { expect($object->name)->toBe('pho'); }); }); });
当需要将多个对象传递到使用use
的闭包时,事情可能会变得有些冗长。为了避免这样长的参数列表,可以使用$this
在套件和规格之间设置和检索值。
describe('SomeClass', function() { $this->key1 = 'initialValue'; $this->key2 = 'initialValue'; context('methodOne()', function() { $this->key1 = 'changedValue'; it('contains a spec', function() { expect($this->key1)->toBe('changedValue'); expect($this->key2)->toBe('initialValue'); }); }); context('methodTwo()', function() { it('contains another spec', function() { expect($this->key1)->toBe('initialValue'); expect($this->key2)->toBe('initialValue'); }); }); });
钩子可用于运行作为设置和清理的操作函数。before
在套件中的任何规格之前运行,而after
在套件中的所有规格都已运行后运行。beforeEach
和afterEach
每个规格都运行它们的闭包一次。请注意,beforeEach
和afterEach
都是可堆叠的,并将应用于嵌套套件中的规格。此外,可以在启动文件中定义全局钩子。例如,启动文件中的afterEach钩子将在套件中的每个测试之后运行。
describe('Suite with Hooks', function() { $this->count = 0; beforeEach(function() { $this->count = $this->count + 1; }); it('has a count equal to 1', function() { expect($this->count)->toEqual(1); // A single beforeEach ran }); context('nested suite', function() { beforeEach(function() { $this->count = $this->count + 1; }); it('has a count equal to 3', function() { expect($this->count)->toEqual(3); // Both beforeEach closures incremented the value }); }); });
运行规格
默认情况下,pho在当前工作目录下的test
或spec
文件夹中查找规格。它将递归遍历所有子文件夹,并运行以Spec.php
结尾的任何文件,即:userSpec.php。此外,使用--watch
选项进行持续测试非常简单,该选项将监视路径中的所有文件以查找更改,并在保存时重新运行规格。
期望/匹配器
类型匹配
expect('pho')->toBeA('string'); expect(1)->notToBeA('string'); expect(1)->not()->toBeA('string'); expect(1)->toBeAn('integer'); // Alias for toBeA expect('pho')->notToBeAn('integer'); expect('pho')->not()->toBeA('integer');
实例匹配
expect(new User())->toBeAnInstanceOf('User'); expect(new User())->not()->toBeAnInstanceOf('Post'); expect(new User())->notToBeAnInstanceOf('Post');
严格相等匹配
expect(true)->toBe(true); expect(true)->not()->toBe(false); expect(true)->notToBe(false); expect(['foo'])->toEqual(['foo']); // Alias for toBe expect(['foo'])->not()->toEqual(true); expect(['foo'])->notToEqual(true);
宽松相等匹配
expect(1)->toEql(true); expect(new User('Bob'))->not()->ToEql(new User('Alice')) expect(new User('Bob'))->notToEql(new User('Alice'))
长度匹配
expect(['tdd', 'bdd'])->toHaveLength(2); expect('pho')->not()->toHaveLength(2); expect('pho')->notToHaveLength(2); expect([])->toBeEmpty(); expect('pho')->not()->toBeEmpty(); expect('pho')->notToBeEmpty();
包含匹配
expect('Spectacular!')->toContain('Spec'); expect(['a', 'b'])->not()->toContain('c'); expect(['a', 'b'])->notToContain('c'); expect('testing')->toContain('test', 'ing'); // Accepts multiple args expect(['tdd', 'test'])->not()->toContain('bdd', 'spec'); expect(['tdd', 'test'])->notToContain('bdd', 'spec'); expect(['name' => 'pho'])->toHaveKey('name'); expect(['name' => 'pho'])->not()->toHaveKey('id'); expect(['name' => 'pho'])->notToHaveKey('id');
模式匹配
expect('tdd')->toMatch('/\w[D]{2}/i'); expect('pho')->not()->toMatch('/\d+/'); expect('pho')->notToMatch('/\d+/'); expect('username')->toStartWith('user'); expect('spec')->not()->toStartWith('test'); expect('spec')->notToStartWith('test'); expect('username')->toEndWith('name'); expect('spec')->not()->toEndWith('s'); expect('spec')->notToEndtWith('s');
数字匹配
expect(2)->toBeGreaterThan(1); expect(2)->not()->toBeGreaterThan(2); expect(1)->notToBeGreaterThan(2); expect(2)->toBeAbove(1); // Alias for toBeGreaterThan expect(2)->not()->toBeAbove(2); expect(1)->notToBeAbove(2); expect(1)->toBeLessThan(2); expect(1)->not()->toBeLessThan(1); expect(2)->notToBeLessThan(1); expect(1)->toBeBelow(2); // Alias for toBeLessThan expect(1)->not()->toBeBelow(1); expect(2)->notToBeBelow(1); expect(1)->toBeWithin(1, 10); // Inclusive expect(-2)->not()->toBeWithin(-1, 0); expect(-2)->notToBeWithin(-1, 0);
打印匹配
$callable = function() { echo 'test' }; expect($callable)->toPrint('test'); expect($callable)->not()->toPrint('testing'); expect($callable)->notToPrint('testing');
异常匹配
$callable = function() { throw new Custom\Exception('error!'); }; expect($callable)->toThrow('Custom\Exception'); expect($callable)->not()->toThrow('\ErrorException'); expect($callable)->notToThrow('\ErrorException');
自定义匹配器
可以通过创建一个实现pho\Expectation\Matcher\MatcherInterface
的类并使用pho\Expectation\Expectation::addMatcher()
注册匹配器来添加自定义匹配器。以下是一个基本匹配器的示例
namespace example; use pho\Expectation\Matcher\MatcherInterface; class ExampleMatcher implements MatcherInterface { protected $expectedValue; public function __construct($expectedValue) { $this->expectedValue = $expectedValue; } public function match($actualValue) { return ($actualValue === $this->expectedValue); } public function getFailureMessage($negated = false) { if (!$negated) { return "Expected value to be {$this->expectedValue}"; } else { return "Expected value not to be {$this->expectedValue}"; } } }
注册它
use pho\Expectation\Expectation; // Register the matcher Expectation::addMatcher('toHaveValue', '\example\ExampleMatcher');
就是这样!您现在可以访问以下内容
expect($actual)->toHaveValue($expected); expect($actual)->not()->toHaveValue($expected); expect($actual)->notToHaveValue($expected);
报告器
点(默认)
$ pho --reporter dot exampleSpec.php
.FI
Failures:
"A suite can have specs that fail" FAILED
/Users/danielstjules/Desktop/exampleSpec.php:9
Expected false not to be false
Finished in 0.00125 seconds
3 specs, 1 failure, 1 incomplete
规格
$ pho --reporter spec exampleSpec.php
A suite
contains specs with expectations
can have specs that fail
can have incomplete specs
Failures:
"A suite can have specs that fail" FAILED
/Users/danielstjules/Desktop/exampleSpec.php:9
Expected false not to be false
Finished in 0.0012 seconds
3 specs, 1 failure, 1 incomplete
列表
$ pho --reporter list exampleSpec.php
A suite contains specs with expectations
A suite can have specs that fail
A suite can have incomplete specs
Failures:
"A suite can have specs that fail" FAILED
/Users/danielstjules/Desktop/exampleSpec.php:9
Expected false not to be false
Finished in 0.0012 seconds
3 specs, 1 failure, 1 incomplete
模拟
Pho 目前没有提供开箱即用的模拟/存根。相反,建议使用如 prophecy 或 mockery 这样的模拟框架。
注意:测试可以通过测试钩子失败。如果在运行规范后需要检查模拟对象的期望值,可以从 aftereach 钩子中进行。
describe('A suite', function() { afterEach(function() { Mockery::close(); }); it('should check mock object expectations', function() { $mock = Mockery::mock('simplemock'); $mock->shouldReceive('foo')->with(5)->once()->andReturn(10); expect($mock->foo(5))->toBe(10); }); });
命名空间
如果您不想让 pho 使用全局命名空间来执行其函数,可以将 --namespace
标志设置为强制其仅使用 pho 命名空间。这对于 PHP 5.6 中的 https://wiki.php.net/rfc/use_function 将是一个更好的选择。
pho\describe('A suite', function() { pho\it('contains specs with expectations', function() { pho\expect(true)->toBe(true); }); pho\it('can have specs that fail', function() { pho\expect(false)->not()->toBe(false); }); });