行为驱动测试框架

1.2.1 2017-04-24 04:47 UTC

This package is not auto-updated.

Last update: 2024-09-14 15:32:18 UTC


README

pho

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

Build Status

安装

以下说明概述了使用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,包括以下函数: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在套件中的任何规格之前运行,而after在套件中的所有规格都已运行后运行。beforeEachafterEach每个规格都运行它们的闭包一次。请注意,beforeEachafterEach都是可堆叠的,并将应用于嵌套套件中的规格。此外,可以在启动文件中定义全局钩子。例如,启动文件中的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在当前工作目录下的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 这样的模拟框架。

注意:测试可以通过测试钩子失败。如果在运行规范后需要检查模拟对象的期望值,可以从 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);
    });
});