Laravel 中的行为驱动开发
发布于 作者 Steve McDougall
BDD 或行为驱动开发,是许多组织中流行的测试方法,并且在将跨团队的测试工作统一起来方面拥有经过验证的成功记录。但问题仍然是,如何在 Laravel 中实现这一点,而无需学习新的测试框架或新的语言语法,例如 Gherkin。
作为一个企业,能够以易于阅读的方式定义流程,并在我们的测试套件中体现出来,这是一个巨大的优势。就像领域驱动设计允许我们为代码创建无所不在的语言一样,BDD 将使我们能够为测试拥有无所不在的语言。
让我们来浏览一些 BDD 测试的示例,然后对其进行分解。假设我们有一个具有注册表单的 Web 应用程序。当完成此表单后,我们希望用户将被注册,并且他们应该自动登录。让我们在典型的功能测试中看看这一点
it('allows a user to register for an account', function (string $email) { expect( User::query()->count(), )->toEqual(0); post( route('register'), ['name' => 'test', 'email' => $email, 'password' => 'password'] )->assertRedirect(route('dashboard')); expect( User::query()->count(), )->toEqual(1);})->with('emails');
这是一个使用 pestPHP 测试此端点的简单示例,它复制表单提交。如您所见,作为开发人员,如果您习惯于使用 pest 进行测试,这相对容易理解。但是,您的 QA 工程师会对此感到困惑,因为他们不习惯使用 pestPHP,而且它没有他们理解的语法。
我们如何重构它以使用 BDD 和我们的 QA 工程师和更广泛的团队可能理解的语法?幸运的是,pestPHP 插件将允许我们使用“给定、何时、然后”方法,这在 BDD 世界中很常见。这是 给定、何时、然后插件,并且入门非常简单。运行以下 composer 命令安装此插件
composer require milroyfraser/pest-plugin-gwt --dev
从这里开始,我们可以开始为 BDD 编写具体的测试。此时我们要牢记的一件事是,我们是想替换我们的测试,还是想让 BDD 增强我们当前的测试套件?我建议改进现有的测试套件,以避免丢失有价值的测试。
让我们以我最近遇到的一个例子为例。我并没有为我的 Laravel 应用程序使用任何特定的 Auth 包。相反,我需要创建一个自定义身份验证流程 - 使用一次性密码。我的注册表单是一个 Livewire 组件,它为我处理逻辑。所以,让我们先写一个功能测试,以确保我们的组件正常工作。
it('will submit the form and create a new user', function (string $email) { Livewire::test( RegisterForm::class, )->set( 'name', 'test', )->set( 'email', $email, )->set( 'password', 'password', )->call( 'submit' )->assertHasNoErrors( ['name', 'email', 'password'] );})->with('emails');
我们正在测试我们是否可以填写并提交表单。我们可以在其中添加关于此的预期结果,以确保用户在数据库中创建,但我们可以在此简化我们的功能测试,并将其中一些逻辑转移到集成测试中。
在我们的例子中,就像我的大部分代码一样,我在 Action 类中执行逻辑,所以将它转移是有意义的。我通常为所有需要执行的读写操作创建单个 Action 类,以便 CLI、Web 和 API 可以使用所有类似的逻辑 - 唯一的区别是调用方式。在上面的示例中,我们的 Livewire 组件将调用 Action 来创建用户。
所以现在,让我们看看业务流程在 Gherkin 语法中是什么样子的
Scenario: The Register Action is handled Given the RegisterAction is created When the handle method is called Then a new user will be created
诚然,我们可以用标准测试写这个,这对我们作为开发人员来说是有意义的 - 但我喜欢 DDD 原则之一是您创建的无所不在的语言 - 就像一种商业语言。
对于我们的 BDD 测试,我将在 test
下创建一个 Integration
目录,以便我有:Unit:测试驱动开发 Feature:测试驱动开发 Integration:行为驱动开发
在我们的 Integrations
目录中,我们将使用我们安装的插件将所有创建的场景存储为 pestPHP 测试。
scenario('The RegisterAction is handled') ->given(fn () => new RegisterAction()) ->when(fn (RegisterAction $action) => $action->handle( name: 'test', password: 'password', ))->then(fn () => assertDatabaseHas('users', [ 'name' => 'test', ]));
如您从上面的代码中看到的,它很容易理解。它与我们期望在大多数 BDD 测试套件中看到的内容非常相似 - 但在一个我们习惯的框架中。在许多情况下,我们通常可以直接将它翻译成用户故事。
让我们再举一个例子,但这次我们将从用户故事开始
作为一个用户,当我激活我的帐户时,我必须收到一封电子邮件。
现在让我们将其移动到 Gherkin 语法中
Scenario: A user can activate their account Given a new user When they activate their account Then an email is sent to confirm the activation.
最后,让我们继续使用我们正在测试的插件的 pestPHP
scenario('A user can activate their account') ->given(fn (): User => User::factory()->inactive()->create()) ->when(fn () => Bus::fake()) ->when(fn (User $user): User => $user->activate()) ->then(function (User $user) { Bus::assertDispatched(ActivateUser::class); });
所以您可以看到,这种测试方法对您的测试套件和团队有优势。我并不是说您应该总是使用这种方法 - 但是对于那些关键的业务流程来说,它允许您将流程从企业理解的语言直接映射到您理解的测试套件。
您在应用程序中发现了其他任何改进测试策略的令人兴奋的方式吗?让我们在 Twitter 上知道您的想法!