BDD 而非 TDD:面向结果的测试

2025-06-10

BDD 而非 TDD:面向结果的测试

TDD 不起作用,给我解决方案

我之前谈过 TDD(测试驱动开发),以及如何测试你的框架而不是应用程序的逻辑。

如果您还没有阅读上一个主题,可以在这里找到:https://dev.to/0xrumple/when-tdd-doesnt-click-something-else-should-click-harder-2h9h

BDD(行为驱动开发)来拯救

这一次,我决定无论如何都要进行测试,因此我选择了一种不关注模拟、伪造、接口、依赖注入的方法……而是全部关注检查结果,这就是 BDD 的用途:行为

我試過了嗎?

是的,直到我们(我和我的创意朋友@alhakem)完成这个宿舍管理系统(学生可以搜索/预订房间,管理人员可以批准/拒绝预订并管理他们的宿舍)之后,我才允许自己写这篇文章:

演示

这是整个项目,“features”文件夹包含测试:https://github.com/coretabs/dorm-portal
请随意 PR 或创建问题。

有趣的事实

我开发了一个有点复杂的过滤引擎(其中有 multiCheckbox/singleCheckbox/integer 过滤标准),并使用 BDD 在大约一周内完成了它......所有测试都通过了,哇哦!

整个项目大约用了两个月的时间完成,而且......全部都是绿色的😉

我该如何做 BDD?

这真的很简单:

1. 编写验收标准(场景)

常见的方式是使用 Gherkin 语言,现在不要害怕,因为它非常直观,只需看看这个示例(应该是不言自明的):

Feature: Reservation

    Scenario: As a student
              I want to make reservations
              So that I get a room to stay in

        Given we have 2 dormitories (and 1 room each available to reserve)
        Given two students who reserved nothing

        When create a reservation
        Then quota of the room should decrease
Enter fullscreen mode Exit fullscreen mode

2. 实施步骤

根据您选择的语言/框架,这可能会有所不同;我正在使用 django(当然还有 python),并且有一个很好的方法可以使用behave-django 包来实现。

以下是上述已实施的验收标准:

from behave import given, when, then
from api.engine.models import RoomCharacteristics
from features.steps.factory import (create_alfam_dovec_with_4_rooms,
                                    create_student,
                                    create_reservation)


@given('we have 2 dormitories (and 1 room each available to reserve)')
def arrange(context):
    create_alfam_dovec_with_4_rooms(context)


@given('two students who reserved nothing')
def arrange(context):
    context.user1 = create_student(context, 'Owen')
    context.user2 = create_student(context, 'Tia')


@when('create a reservation')
def act(context):
    context.previous_quota = context.room1.allowed_quota
    context.reservation1 = create_reservation(context.room1, context.user1)


@then('quota of the room should decrease')
def test(context):
    context.room1 = RoomCharacteristics.objects.get(pk=context.room1.id)
    assert context.room1.allowed_quota == context.previous_quota - 1
Enter fullscreen mode Exit fullscreen mode

3. 让它们变得绿色

现在到了编写实际代码的部分,目标是通过配额测试。

from django.db import (models as django_models, transaction)
from .exceptions import NoEnoughQuotaException


class Dormitory(django_models.Model):
    name = django_models.CharField(max_length=60)


class RoomCharacteristics(django_models.Model):
    allowed_quota = django_models.PositiveIntegerField(default=0)

    dormitory = django_models.ForeignKey(
        Dormitory, related_name='room_characteristics', on_delete=django_models.CASCADE)

    def decrease_quota(self):
        if self.allowed_quota == 0:
            raise NoEnoughQuotaException()

        self.allowed_quota -= 1


class Reservation(django_models.Model):
    user = django_models.ForeignKey(
        User, related_name='reservations', on_delete=django_models.CASCADE)

    room_characteristics = django_models.ForeignKey(
        RoomCharacteristics, related_name='reservations', on_delete=django_models.CASCADE)

    @classmethod
    def create(cls, *args, **kwargs):
        room_characteristics = kwargs['room_characteristics']
        result = cls(*args, **kwargs)

        with transaction.atomic():
            room_characteristics.decrease_quota()
            result.save()
            room_characteristics.save()

        return result
Enter fullscreen mode Exit fullscreen mode

真的吗?听起来太无聊了!

不!相信我,因为我在其他项目中工作过,在这些项目中,添加功能或更改一个简单的东西可能会破坏项目中的许多其他部分,它实际上变成了这样:

绘画

图片来源:giphy

您会意识到,一旦您添加了 X 功能,客户就会向您大喊:“注册不起作用”......未经测试的代码很糟糕

何时进行 BDD 而不是 TDD?

执行-bdd-tdd

参考

  1. Gherkin 语言:https://docs.cucumber.io/gherkin/reference/
  2. Pluralsight 上的 BDD 课程:https://www.pluralsight.com/courses/pragmatic-bdd-dotnet
  3. 不同类型的测试说明:https://dev.to/thejessleigh/different-types-of-testing-explained-1ljo
鏂囩珷鏉ユ簮锛�https://dev.to/yaser/bdd-rather-than-tdd-result-oriented-testing-3n67
PREV
了解剪贴板 API,更智能地与用户交互📋
NEXT
关于微软新编程语言 Bosque,你需要知道的一切