codeception/specify

PHPUnit 和 Codeception 的 BDD 代码块

2.0.0 2021-11-22 00:16 UTC

This package is auto-updated.

Last update: 2024-09-08 12:39:33 UTC


README

为 PHPUnit 或 Codeception 提供的 BDD 风格代码块

Latest Stable Version Total Downloads Latest Unstable Version License StandWithUkraine

Specify 允许您以更易读的 BDD 风格编写测试,就像您可能在使用 Jasmine 时所经历的那样。受 Ruby 的 MiniTest 启发,现在您可以将 BDD 和经典 TDD 风格结合在一个测试中。

安装

需要 PHP >= 7.4

  • 使用 Composer 安装
composer require codeception/specify --dev
  • 在您的测试中包含 Codeception\Specify 特性。

用法

$this->specify 方法指定为为您的 PHPUnit 测试添加隔离测试块!

public function testValidation()
{
    $this->assertInstanceOf('Model', $this->user);

    $this->specify('username is required', function() {
        $this->user->username = null;
        $this->assertFalse($this->user->validate(['username']));
    });

    $this->specify('username is too long', function() {
        $this->user->username = 'toolooooongnaaaaaaameeee';
        $this->assertFalse($this->user->validate(['username']));
    });
}

BDD 示例

Specify 支持在 PHPUnit 中使用 describe-itdescribe-should BDD 语法

public function testValidation()
{
    $this->describe('user', function () {
        $this->it('should have a name', function() {
            $this->user->username = null;
            $this->assertFalse($this->user->validate(['username']));
        });
    });

    // you can use chained methods for better readability:
    $this->describe('user')
        ->should('be ok with valid name', function() {
            $this->user->username = 'davert';
            $this->assertTrue($this->user->validate(['username']));
        })
        ->shouldNot('have long name', function() {
            $this->user->username = 'toolooooongnaaaaaaameeee';
            $this->assertFalse($this->user->validate(['username']));
        })
        // empty codeblocks are marked as Incomplete tests
        ->it('should be ok with valid name') 
    ;
}

Specify + Verify 示例

使用 Codeception/Verify 进行更简单的断言

public function testValidation()
{
    $this->specify('username is too long', function() {
        $this->user->username = 'toolooooongnaaaaaaameeee';
        expect_not($this->user->validate(['username']));
    });

    $this->specify('username is ok', function() {
        $this->user->username = 'davert';
        expect_that($this->user->validate(['username']));
    });
}

用例

这个小巧的库通过组织嵌套代码块使您的测试易于阅读。这允许将相似的测试组合在一起,但将它们放在嵌套部分中。

这与 RSpec 或 Mocha 中的 BDD 语法非常相似,但可以在 PHPUnit 中使用

<?php

class UserTest extends PHPUnit\Framework\TestCase 
{
    use Codeception\Specify;

    /** @specify */
    protected $user; // is cloned inside specify blocks

    public function setUp(): void
    {
        $this->user = new User;
    }

    public function testValidation()
    {
        $this->user->name = 'davert';
        $this->specify('i can change my name', function() {
           $this->user->name = 'jon';
           $this->assertEquals('jon', $this->user->name);
        });
        // user name is 'davert' again
        $this->assertEquals('davert', $this->user->name);
    }
}

每个代码块都是隔离的。这意味着对 $this->specify 的调用不会改变测试类的属性值。应使用 @specify 注解标记隔离属性。

specify 块中的失败不会停止您的测试。

<?php
$this->specify('failing but test goes on', function() {
	$this->fail('bye');
});
$this->assertTrue(true);

// Assertions: 2, Failures: 1
?>

如果测试失败,您将在结果中看到规范文本。

隔离

通过为每个 specify克隆对象属性 来实现隔离。只有标记有 @specify 注解的属性才会被克隆。

/** @specify */
protected $user; // cloning

/** 
 * @specify 
 **/
protected $user; // cloning

protected $repository; // not cloning

使用深度克隆方法克隆对象。

如果对象克隆影响性能,请考虑关闭克隆.

默认情况下,Mock 是隔离的

specify 块内定义的 Mock 不会在外部测试中执行,并且外部测试中的 Mock 也不会在代码块中触发。

<?php
$config = $this->createMock(Config::class);
$config->expects($this->once())->method('init');

$config->init();
// success: $config->init() was executed

$this->specify('this should not fail', function () {
    $config = $this->createMock(Config::class);
    $config->expects($this->never())->method('init')->willReturn(null);
    // success: $config->init() is never executed 
});

示例:数据提供者替代方案

<?php
$this->specify('should calculate square numbers', function($number, $square) {
	$this->assertEquals($square, $number*$number);
}, ['examples' => [
		[2,4],
		[3,9]
]]);

您还可以在 examples 参数中使用 DataProvider 函数。

<?php
$this->specify('should calculate square numbers', function($number, $square) {
	$this->assertEquals($square, $number*$number);
}, ['examples' => $this->provider()]);

也可以与真实数据提供者一起使用

<?php
/**
 * @dataProvider someData
 */
public function testExamplesAndDataProvider($param)
{
    $this->specify('should assert data provider', function ($example) use ($param) {
        $this->assertGreaterThanOrEqual(5, $param + $example);
    }, ['examples' => [[4], [7], [5]]]);
}

public function someData()
{
    return [[1], [2]];
}

Before/After

还有 before 和 after 回调,它们作为 specify 的 setUp/tearDown。

<?php
$this->beforeSpecify(function() {
	// prepare something;	
});
$this->afterSpecify(function() {
	// reset something
});
$this->cleanSpecify(); // removes before/after callbacks
?>

API

可用方法

// Starts a specify code block:
$this->specify(string $thing, callable $code = null, $examples = [])

// Starts a describe code block. Same as 'specify' but expects chained 'it' or 'should' methods.
$this->describe(string $feature, callable $code = null)

// Starts a code block. If 'code' is null, marks test as incomplete.
$this->it(string $spec, callable $code = null, $examples = [])
$this->its(string $spec, callable $code = null, $examples = [])

// Starts a code block. Same as 'it' but prepends 'should' or 'should not' into description.
$this->should(string $behavior, callable $code = null, $examples = [])
$this->shouldNot(string $behavior, callable $code = null, $examples = [])

打印选项

对于 PHPUnit,将 Codeception\Specify\ResultPrinter 打印机添加到 phpunit.xml

<phpunit colors="true" printerClass="Codeception\Specify\ResultPrinter">
</phpunit>

推荐

许可证:MIT。