行为驱动测试框架

1.1.1 2015-01-26 01:52 UTC

This package is not auto-updated.

Last update: 2024-09-28 17:49:02 UTC


README

pho

受Jasmine和RSpec启发的PHP BDD测试框架。具有熟悉的语法,并具有监视命令以在开发期间自动重新运行规格。它还可以通过自定义匹配器和报告器进行扩展。

Build Status

安装

以下说明概述了使用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(领域特定语言),包括以下函数:describecontextitbeforeafterbeforeEachafterEachexpect。还有通过xdescribexcontextxit禁用规格和套件的等效函数。

要创建一个套件,可以使用字符串和函数来传递给describecontext。两者可以互换使用,尽管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');
});

intro-screenshot

可以使用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,一旦套件中的所有规格都已运行,就运行afterbeforeEachafterEach在每个规格上运行它们的闭包一次。请注意,beforeEachafterEach都是可堆叠的,并且将应用于嵌套套件中的规格。

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在当前工作目录下的testspec文件夹中查找规格。它将递归遍历所有子文件夹并运行所有以Spec.php结尾的文件,例如:userSpec.php。此外,使用--watch选项进行持续测试非常简单,该选项将监视路径中的所有文件以进行更改,并在保存时重新运行规格。

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目前不提供开箱即用的模拟/存根。相反,建议使用如prophecymockery之类的模拟框架。

注意:测试钩子内不能失败测试。如果您需要在运行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);
    });
});