【问题标题】:How to design code for testability如何设计可测试性代码
【发布时间】:2008-11-25 06:15:04
【问题描述】:

我一直在考虑在我未来创建的任何项目中使用 TDD 并实施适当的测试(只是刚刚开始了解它使你的生活变得多么美好)。因此,在过去的几天里,我一直在 SO 上四处游荡,试图了解如何设计您的应用程序以实现可测试性,但我似乎仍然在为一些想法苦苦挣扎。

我阅读了很多内容,您应该针对接口而不是进行编程。我遇到的主要问题是,您应该创建多少个接口?你应该为你想要测试的所有东西准备一个吗?还是我读错了?

另一件事是使用大量依赖注入,因此您可以模拟您正在注入的部分,而不是使用真实的东西。它是否正确?还是我也离这里很远?

【问题讨论】:

    标签: .net unit-testing testing tdd


    【解决方案1】:

    我认为你的想法是正确的,但我认为你正在把它变成一个更大的交易。如果您开始进行 TDD,您的第一反应可能是“这是它吗?”。稍后,你应该说“啊哈”!

    主要是你获得 nUnit,学习教程,然后确保在编写实现之前为你所做的一切编写测试。您可能会跳过为属性访问器编写测试,但任何需要任何计算的东西,您都应该先编写测试。

    所以,假设您正在测试一个计算器的 Add(int 1, int 2),您首先想到的是,“我该如何破解它”。我认为可能出错的地方是:负数、零和溢出。诀窍是想象要创建 Add() 方法的人可能犯的每一个错误,然后针对它编写一个测试。所以,我可能会写:

    Assert.AreEqual(5, calc.Add(2, 3), "Adding positives not as expected");
    Assert.AreEqual(-5, calc.Add(-2, -3), "Adding negatives not as expected");
    Assert.AreEqual(-2, calc.Add(-3, 2), "Adding one positive and one negative not as expected");
    
    // your framework might provide a cleaner way of doing this:
    try {
      int result = calc.Add(Int32.Max, 5);
      Assert.Fail("Expected overflow error. Received: " + result);
    } catch(Exception e) {
      // This should be a more specific error that I'm not looking up
    }
    

    因此,如您所见,我尝试做的是找出 Add() 方法可能无法工作的原因,然后对其进行测试。我还寻找了有趣的极端情况并明确定义了我所期望的行为。然后现在我可以开始编写 Add() 方法了。

    现在,虽然这不是那么好,但您知道您的 Add() 方法将坚如磐石,因为当您开始创建将 Sin() 方法与 Sqrt() 方法以及 Add () 方法。

    无论好坏,这都是测试驱动开发。现在不要太纠结于接口或依赖注入。如果您需要,可以稍后再提供。

    【讨论】:

      【解决方案2】:

      首先.. TDD 不是魔法/银弹。 花点时间在这上面..这将是值得的。不要试图在动态或非 SO-posts 中获取 TDD。拿起一本好书(Kent Beck / Dave Astels),然后继续努力……(当人们只需要退后一步阅读时,看到人们四处奔波真是令人沮丧。)

      可测试性设计:

      • 首先编写代码测试..您的可测试设计将会出现..一次通过一个测试。你会得到它作为副产品..更像是做对的一种享受。
      • 一厢情愿:想象一下您要构建的东西已经存在。你会怎么跟它说话?这个简单的模拟应该会为您提供“事物”的外部接口定义
      • 保持简单/YAGNI/握住锤子:花一些时间了解这些实践(模式、DI、Mocks,你会发现很多等),首先找出它们存在的原因。评估您是否需要它们,然后才继续使用它们。另一个要寻找的好东西是“哪里不使用它们?”。如果这听起来像一个坏主意..它可能是。 (例如,我是否需要一个接口来为我编写的每个类做 PR.. 可能不需要。我需要 Spring.net 我创建的每个类吗?)在每个检查点问以下问题(K. beck 对简单设计的定义)

        • 代码和测试是否传达了我需要传达的一切信息?
        • 是否有重复? (如果有则删除)
        • 它是否包含最少的类?...最少的方法?有什么我可以从这个设计中拿走并且仍然保留行为的东西。
      • 无情地重构:听 Martin Fowler 的话,把书放在手边

      (这是一个有趣的过程,试图将您所做的事情提炼成几个步骤:)感谢您有机会反思)

      【讨论】:

      • @Nathan.. 我住在印度.. 大多数新书根本没有出现。是的,它很贵..但我会毫不犹豫地买一些这些书,因为它们很好..如果我考虑到它们通过让我的工作更好而节省的时间..它的回报可观。 TDDbE 就是其中之一
      • @Gishu 我完全同意它值得买好书,只是大多数时候我没有钱买它们,但嘿,无论如何我都会买这本书...... .我什至可以找工作给我买它;)
      【解决方案3】:

      我喜欢使用 Kent Beck 的经验法则。

      编写一个描述您理想的对象 API 的测试。换句话说,编写您希望其他人可以编写的代码以使某些事情发生。您最终可能无法以这种方式实际实现它,但您最好从一开始就使用 BEST 接口而不是其他东西。

      另外,只是一点测试提示...不要想太多。有很多人会告诉你如何测试,但根据我的经验,你是唯一能真正做出决定的人。只要知道您正在测试的事实就是让您的代码变得更好,并做正确的事情。当您查看其他经过良好测试的代码时,您将开始开发一种风格并立即成为专业人士。

      希望这会有所帮助。

      【讨论】:

        【解决方案4】:

        首先,阅读11月24日刚刚发布的Google Guide to Writing Testable Code

        它是 Google 测试传播者 Miško Hevery 对可测试性最佳实践的精彩汇编。

        【讨论】:

          【解决方案5】:

          您正朝着正确的方向前进,但不要太疯狂。例如,您不需要为要测试的每个对象提供接口。

          了解您描述的两种技术的主要动机是减少系统中的耦合。紧密耦合的系统很难测试,因为我无法检测正确/不正确的行为,而且我无法防止实际系统中的副作用(访问文件、数据库等)。

          (事实证明,紧密耦合也使系统难以维护,因此以测试为目的而努力打破耦合也带来了其他好处,这就是为什么这么多人强调单元测试和 TDD 的设计好处特别是。)

          【讨论】:

            【解决方案6】:

            如果您应用 TDD,您将获得可测试且经过测试的设计,因为您将与代码一起扩展单元测试套件。由测试驱动的设计出现

            找到一个粗略的设计(*)来解决手头的问题,然后从一个不依赖另一个类的类开始,实现它,先测试。然后你可以实现一个也没有依赖的类,或者一个依赖于你已经实现的类的类。

            当实现一个依赖于另一个类的类时,你有两种测试它的可能性:要么使用另一个类的实例,要么模拟它,在这种情况下,拥有一个接口可能会有所帮助。使用不支持接口的语言,您可以只对要模拟的类进行子类化。

            当您不希望您正在编码的类依赖于另一个类时,最好针对接口进行编程,而不希望依赖于另一个类以属于另一个层或另一个组件。

            (*) 您不必设计所有东西,因为测试驱动您的代码可能会引导您进入另一个方向:请参阅 bowling game 文章,该文章演示了 TDD紧急设计 在几页中。

            【讨论】:

              【解决方案7】:

              如果你真的按照你说的做的话,你对 Mocking 部分的看法是正确的。

              至于接口,考虑到这一点,我个人的开发方法是我首先编写应用程序的主路径应该是什么样子(使用我理想的什么都不做的模拟 API)。然后在查看了我的 Mock 实现并对其进行了几次改进之后,我开始了实际的类设计。在这个阶段“知道”我如何测试每个方法,并为它编写一个测试,如果由于某种原因我遇到一个方法我不能轻易弄清楚如何测试,这意味着该方法不好,大多数可能它有太多的责任,太多的中间状态我无法测试,所以我分解了方法(接口胜过类设计)。并继续滴下,直到我有一个可行的应用程序。

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2013-06-12
                • 1970-01-01
                • 1970-01-01
                • 2012-03-29
                • 2014-09-30
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多