【问题标题】:Test-Driven Development - How to write a test before none of implementation code exists测试驱动开发 - 如何在不存在任何实现代码之前编写测试
【发布时间】:2014-01-13 03:49:32
【问题描述】:

我正在学习 TDD,但很难采用它,因为它并不简单。

我无法回答的问题是“如何在任何实现代码存在之前编写测试?”。

如果我们的目标类/目标方法/目标参数类型/目标返回类型不存在,

  • 在测试中编写代码时我们指的是什么。我们如何开始编写测试?
  • 如果我们能写的只是实际实现代码之前的测试方法名称,测试怎么会失败?

每个人都说为什么而不是如何

我已尽我所能找到详细说明在生产代码之前编写测试的资源,但假设我错过了好的资源,其中大部分都充满了陈词滥调 解释为什么 TTD 比专注于采用它的实践更重要。

一个示例用例。

假设我们正在为一所大学开发软件,我们的用例是课程注册。

为了简单起见,让我们把这个讨论限制在

  • 场景:“一个学生每学期最多可以注册任意 3 门课程”
  • 测试服务层和dao层。

伪代码

ENROLL(studentId, courseId)
    //check if student enrolled in less than 3 courses in the same semester as given courseId belongs in.
    //if yes, enroll him/her.
    //if not, return an error.

上面的实际实现可能跨越几个涉及服务、daos 等的类。

请您解释一下如何逐步测试驱动开发它?如果你要使用 TDD 来实现它,你是如何一步一步地做到的。

我希望这可以帮助将来像我这样的许多斗争。

【问题讨论】:

  • 你知道这个问题是开放式的,会有不同的答案吗?
  • 您需要坐下来阅读一本书(或至少观看一些视频)。我推荐测试驱动开发:Kent Beck 的示例。
  • 感谢您的回复。是的,我意识到这是一个开放式问题。只是想看看人们如何解决同样的问题,并可能帮助我和许多人走上正轨。
  • 这是一个很好的问题。即使我已经很久了。正因为如此,许多开发人员在没有 TDD 的情况下离开了。

标签: java unit-testing junit tdd


【解决方案1】:

在 src/test/java 和 EnrollingService 同一个包中创建 EnrollingServiceTest 类

class EnrollingServiceTest {
    private EnrollingService enrollingService;

    @Before 
    public void init() {
           enrollingService = new EnrollingService();
    }

    @Test
    public void testEnroll() {
           boolean result = enrollingService.enroll(1l, 1l);
           assertTrue(result);
    ...

IDE(我假设您使用的是 IDE)显示错误 - EnrollingService 不存在。

EnrollService 上的光标点 - IDE 将提供创建一个类 - 让它在 src/main/java 中创建

现在 IDE 说,enroll(long, long) 方法丢失 - 让 IDE 为您创建它。

现在 IDE 没有显示错误。运行测试 - 它失败了。去注册,开始实现逻辑

等等……

【讨论】:

  • 现在我终于明白“失败的测试”也意味着由于编译错误而失败。高超!非常感谢。您的回复确实阐明了 TDD 中的实际步骤顺序。
  • @phani,我认为这不仅仅是因为编译错误导致的失败。它关于由于测试用例失败而发生的错误。
  • 或者,如果您希望使用更自我描述的方法,您可以尝试Ginkgo4j,您最终会得到看起来更像this的东西
【解决方案2】:

当您关注代码的预期行为而不是代码的实现时,这一点会变得更加清晰。因此,鉴于您概述的场景,您可能会得出结论,您必须在某个类中编写enroll() 方法。然后你可以考虑如何测试这个类。

您首先要考虑班级的条件以及对班级的期望。也许您可以识别类的某些不变量。在这种情况下,要测试该类,您需要考虑违反该不变量的方式。

因此声明:一个学生每学期最多可以注册 3 门课程,您考虑一下这种情况可能发生的方式。

  1. 学生在给定学期注册了0门课程,尝试注册课程,结果:注册成功;学生现在已注册给定学期的 1 门课程。
  2. 学生在给定学期注册了1门课程,尝试注册课程,结果:注册成功;学生现在注册了给定学期的 2 门课程。
  3. 学生在给定学期注册了 3 门课程,尝试注册课程,结果:失败(可能抛出异常?)
  4. 等等等等

接下来您实际编写这些测试。这些中的每一个都可以是一种测试方法。因此,测试方法将确保创建对象并按预期设置环境。然后调用该方法并将结果与​​预期结果进行比较。如果你期望发生的事情确实发生了,那么测试就通过了。

现在,最初,由于您尚未编写测试实际上不会通过的方法。但是当您开始编写代码时,您的测试将开始通过,最终 100% 的测试将通过,此时您对自己的代码满足要求感到满意。

【讨论】:

    【解决方案3】:
    public void shouldNotEnrollInMoreThanFourClassesInASemester() {
      Enroller enroller = new Enroller();
      Student student = new Student();
      Semester one = new Semester();
      Semester two = new Semester();
      Course geology = new Course(one);
      Course architecture = new Course(one);
      Course calculus = new Course(one);
      Course sociology = new Course(one);
      Course geometry = new Course(two);
    
      assertOk(enroller.enroll(student, geology));
      assertOk(enroller.enroll(student, architecture));
      assertOk(enroller.enroll(student, calculus));
      assertNotOk(enroller.enroll(student, sociology));
      assertOk(enroller.enroll(student, geometry));
    }
    

    【讨论】:

    • 您的测试可能适用于服务层,但不是 dao,我相信,因为您是基于不同的表进行测试,
    【解决方案4】:

    在您的场景中,您应该分别测试每一层,因此在测试服务层时模拟出 dao。

    当你第一次编写测试时,它不会编译,这意味着它失败了,但这很好,因为类不存在。

    在您的示例中,应强制哪个层最多注册 3 门课程?这将影响您的测试方式。

    首先编写测试将帮助您解决这些类型的问题。

    如前所述,这对于一个明确的答案来说过于开放,但如果您开始编写测试,然后将其作为更新发布,它可能会有所帮助。

    所以,编写你的 dao 测试,然后编写类和方法以便它编译,但它仍然会失败,直到 yiu 完成实现。您可能需要测试 2、3、4 类注册并确保每个都适当地失败,然后完成实现。

    【讨论】:

    • +1,感谢您的回复,尽管我的问题很模糊,而且很难找到明确的答案。
    猜你喜欢
    • 1970-01-01
    • 2011-03-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多