artox-lab / pho
行为驱动测试框架
Requires
- php: >=5.4.0
This package is not auto-updated.
Last update: 2024-09-28 17:49:02 UTC
README
受Jasmine和RSpec启发的PHP BDD测试框架。具有熟悉的语法,并具有监视命令以在开发期间自动重新运行规格。它还可以通过自定义匹配器和报告器进行扩展。
安装
以下说明概述了使用Composer进行安装的过程。如果您没有Composer,可以从https://getcomposer.org.cn/下载。
- 根据您的环境,运行以下任一命令:
$ composer global require danielstjules/pho:dev-master
$ php composer.phar global require danielstjules/pho:dev-master
- 编辑您的
~/.bash_profile
或~/.profile
并添加:
export PATH=$HOME/.composer/vendor/bin:$PATH
使用
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
都是可堆叠的,并且将应用于嵌套套件中的规格。
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之类的模拟框架。
注意:测试钩子内不能失败测试。如果您需要在运行spec后检查模拟对象的期望,请确保在spec正文中进行。以下示例使用$teardown
闭包来实现,尽管名称并不重要。
describe('A suite', function() { // Any last checks that could fail a test would go here $this->teardown = 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); $this->teardown(); }); });
命名空间
如果您不希望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); }); });