初学者问题和绊脚石

虽然我为大部分代码编写了单元测试,但最近我只通过Kent Beck举例说明了TDD的副本。 我一直对我做出的某些设计决定感到后悔,因为它们阻止了应用程序的'可测试'。 我读了这本书,虽然有些看起来很陌生,但我觉得我可以管理它,并决定尝试一下我当前的项目,这个项目基本上是一个客户端/服务器系统,两个部分通过这个系统进行通信。 USB。 一个在小工具上,另一个在主机上。 该应用程序是在Python中。

我开始了,很快就陷入了一堆重写和小测试,后来我发现它们并没有测试任何东西。 我扔掉了其中的大部分,现在有一个工作应用程序,其中的测试都凝结成了2。

根据我的经验,我想问一些问题。 我从New到TDD获得了一些信息:是否有带测试的示例应用程序来展示如何执行TDD? 但有一些具体的问题我想回答/讨论。

  • Kent Beck使用他添加并列出的列表来指导开发过程。 你如何制作这样的清单? 我最初有几个项目,如“服务器应该启动”,“服务器应该中止,如果渠道不可用”等,但他们混合,最后现在,它只是像“客户端应该能够连接到服务器”(这包含的服务器启动等)。
  • 你如何处理重写? 我最初选择了一个基于命名管道的半双工系统,以便我可以在自己的机器上开发应用程序逻辑,然后添加USB通信部分。 它们变成了基于套接字的东西,然后从使用原始套接字转移到使用Python套接字服务器模块。 每当事情发生变化,我发现我不得不重写相当多的令人讨厌的测试部分。 我认为测试在我的开发过程中会是一个不变的指导。 他们只是想要更多的代码来处理。
  • 我需要一个客户端和一台服务器通过通道进行通信来测试任何一方。 我可以嘲笑其中一方来测试另一方,但那么整个频道将不会被测试,我担心我会错过这一点。 这减损了整个红/绿/重构节奏。 这只是缺乏经验还是我做错了什么?
  • “直到你做出来为止”这个代码给我留下了许多混乱的代码,后来我花了很多时间来重构和清理。 这是事情的方式吗?
  • 在会议结束时,我现在有我的客户端和服务器运行大约3或4个单元测试。 我花了一个星期左右才做到这一点。 如果我使用代码方式使用单元测试,我想我可以在一天内完成它。 我看不到收益。
  • 我正在寻找使用此方法完全(或几乎完全)实施大型非平凡项目的人的意见和建议。 在我有一些东西已经运行并且想要添加一个新功能之后,遵循这种方式是有道理的,但是从头开始看起来很麻烦并且不值得付出努力。

    PS:请让我知道这是否应该是社区wiki,我会这样标记。

    更新0:所有的答案都同样有帮助。 我选择了我所做的一个,因为它与我的经验最为共鸣。

    更新1 :练习练习练习!


    作为一个初步评论,TDD需要实践。 当我回顾我在开始TDD时所写的测试时,发现很多问题,就像我看几年前写的代码一样。 继续这样做,就像你开始认识不错的代码一样,同样的事情会在你的测试中发生 - 耐心。

    你如何制作这样的清单? 我最初有几个项目,如“服务器应该启动”,“服务器应该中止,如果渠道不可用”等,但他们混合,最后现在,它只是像“客户端应该能够连接到服务器”

    “列表”可以是非常非正式的(贝克的书中就是这种情况),但是当你将这些项目转化为测试时,试着将这些陈述编写成一个“[当这个事情发生时] [这个条件应该是真的]]格式。 这会迫使你更多地考虑你正在验证的内容,你将如何验证它,并直接转化为测试 - 或者它不应该让你知道哪些功能缺失。 考虑用例/场景。 比如“服务器应该启动”不清楚,因为没有人正在发起一个动作。

    每当事情发生变化,我发现我不得不重写相当多的令人讨厌的测试部分。 我认为测试在我的开发过程中会是一个不变的指导。 他们只是想要更多的代码来处理。

    首先,是的,测试是更多的代码,并且需要维护 - 并且编写可维护的测试需要练习。 我同意S.洛特的观点,如果你需要改变你的测试,你可能会测试“太深”。 理想情况下,您希望在公共接口级别进行测试,该级别不可能发生变化,而不是在可能演变的实现细节级别进行测试。 但是练习的一部分就是想出一个设计,所以你应该期望得到一些错误,并且也必须移动/重构你的测试。

    我可以嘲笑其中一方来测试另一方,但那么整个频道将不会被测试,我担心我会错过这一点。

    不完全确定那一个。 从它的声音来看,使用模拟是一个正确的想法:采取一方,嘲笑另一方,并检查每一方的工作,假设另一方得到正确实施。 一起测试整个系统是集成测试,你也想做,但通常不是TDD过程的一部分。

    “直到你做出来为止”这个代码给我留下了许多混乱的代码,后来我花了很多时间来重构和清理。 这是事情的方式吗?

    在进行TDD时,您应该花费大量时间进行重构。 另一方面,当你伪造它时,这是暂时的,你的下一步应该是去伪造它。 通常情况下,你不应该有多个测试通过,因为你伪造了它 - 你应该一次专注于一件事,并且尽可能的重构它。

    如果我使用代码方式使用单元测试,我想我可以在一天内完成它。 我看不到收益。

    再一次,它需要练习,并且随着时间的推移应该会更快。 此外,有时TDD比其他人更有成效,我发现在某些情况下,当我完全知道要写入的代码时,只需编写好大部分代码然后编写测试即可。
    除了贝克之外,我喜欢的一本书是罗伊·奥谢罗夫的单元测试艺术。 这不是一本TDD书,它是面向.Net的,但是你可能想看看它:很好的一部分是关于如何编写可维护的测试,测试质量和相关的问题。 我发现这本书在写完测试之后,以我的经验产生了共鸣,有时候还在努力做正确的事情......
    所以我的建议是,不要把毛巾扔得太快,并给它一些时间。 您可能还想简单一些,测试服务器通信相关的东西听起来不像是最简单的项目开始!


  • 肯特贝克使用一个列表......终于,现在,它就像“客户端应该能够连接到服务器”(这包括服务器启动等)。
  • 通常是一种不好的做法。

    对架构的每个独立层进行单独测试都很好。

    综合测试往往掩盖架构问题。

    但是,只测试公共职能。 并非每个功能。

    并且不要花费很多时间来优化您的测试。 测试中的冗余不会像工作应用程序中那样严重。 如果事情发生了变化,一个测试工作,但另一个测试中断,那么你可能会重构你的测试。 之前没有。

    2.你如何处理重写? ...我发现我必须重写相当多的测试部分。

    你正在测试的细节水平太低。 测试最外面的,公开的,可见的界面。 应该是不变的部分。

    是的,重大的架构变化意味着重大的测试变更

    测试代码是你如何证明事情的工作。 它与应用程序本身几乎同样重要。 是的,这是更多的代码。 是的,你必须管理它。

    3.我需要一个客户端和一台服务器通过通道进行通信来测试任何一方。 我可以嘲笑其中一方来测试另一方,但是整个通道不会被测试......

    有单元测试。 嘲笑。

    有集成测试,测试整个事情。

    不要混淆他们。

    你可以使用单元测试工具来进行集成测试,但它们是不同的东西。

    你需要同时做两件事。

    4.“直到你把它伪造”给我留下了许多混乱的代码,后来我花了很多时间来重构和清理。 这是事情的方式吗?

    是。 这正是它的工作原理。 从长远来看,有些人认为这比效力他们的大脑试图事先做好所有设计更有效。 有些人不喜欢这一点,并希望事先做好所有的设计; 如果你愿意,你可以自由地预先做很多设计。

    我发现重构是一件好事,前台设计太难了。 也许是因为我已经写了将近40年了,而且我的大脑正在磨损。

    5.我看不到收益。

    所有真正的天才都发现测试减慢了他们的速度。

    我们其他人不能确定我们的代码是否有效,除非我们有一套完整的测试证明它可行。

    如果你不需要证明你的代码有效,你就不需要测试。


    问:Kent Beck使用他添加并列出的列表来指导开发过程。 你如何制作这样的清单? 我最初有几个项目,如“服务器应该启动”,“服务器应该中止,如果渠道不可用”等,但他们混合,最后现在,它只是像“客户端应该能够连接到服务器”(这包含的服务器启动等)。

    我开始挑选我可能会检查的任何东西。 在你的例子中,你选择了“服务器启动”。

    Server starts
    

    现在我寻找可能想写的更简单的测试。 变化少,移动部件少。 例如,我可能会考虑“正确配置服务器”。

    Configured server correctly
    Server starts
    

    实际上,“服务器启动”取决于“正确配置的服务器”,因此我明确指出了该链接。

    Configured server correctly
    Server starts if configured correctly
    

    现在我寻找变化。 我问:“什么可能会出错?” 我可以错误地配置服务器。 有多少种不同的方式是重要的? 每个人都进行测试。 即使我配置正确,服务器如何启动? 每种情况都会进行测试。

    问:你如何处理重写? 我最初选择了一个基于命名管道的半双工系统,以便我可以在自己的机器上开发应用程序逻辑,然后添加USB通信部分。 它们变成了基于套接字的东西,然后从使用原始套接字转移到使用Python套接字服务器模块。 每当事情发生变化,我发现我不得不重写相当多的令人讨厌的测试部分。 我认为测试在我的开发过程中会是一个不变的指导。 他们只是想要更多的代码来处理。

    当我改变行为时,我发现改变测试是合理的,甚至是先改变它们! 如果我必须更改不直接检查我正在更改的行为的测试,那么这是一个迹象,表明我的测试依赖于太多不同的行为。 这些是综合测试,我认为这是一个骗局。 (谷歌“集成测试是一个骗局”)

    问:我需要一个客户端和一台服务器通过通道进行通信以测试任何一方。 我可以嘲笑其中一方来测试另一方,但那么整个频道将不会被测试,我担心我会错过这一点。 这减损了整个红/绿/重构节奏。 这只是缺乏经验还是我做错了什么?

    如果我建立一个客户端,一个服务器和一个通道,那么我试着单独检查每一个。 我从客户端开始,当我测试驱动它时,我决定服务器和通道如何表现。 然后我实现每个通道和服务器以匹配我需要的行为。 在检查客户端时,我将频道存根; 当检查服务器时,我嘲笑这个频道; 当检查通道时,我存根并模拟客户端和服务器。 我希望这对你有意义,因为我必须对这个客户端,服务器和通道的性质做出一些认真的假设。

    问:“直到你做出来为止”,给我留下了很多乱码,后来我花了很多时间来重构和清理。 这是事情的方式吗?

    如果你在清理之前让你的“伪造”代码变得非常混乱,那么你可能花了太长时间来伪造它。 也就是说,我发现尽管我最终用TDD清理了更多的代码,但整体节奏感觉好多了。 这来自练习。

    问:在会议结束时,我现在有我的客户端和服务器运行大约3或4个单元测试。 我花了一个星期左右才做到这一点。 如果我使用代码方式使用单元测试,我想我可以在一天内完成它。 我看不到收益。

    我不得不说,除非你的客户端和服务器非常非常简单,否则你需要每次3或4次以上的测试来彻底检查它们。 我猜测你的测试会一次检查(或者至少执行)一些不同的行为,这可能是你编写它们的努力的原因。

    另外,不要测量学习曲线。 我第一次真正的TDD经历包括在9,14小时内重写3个月的工作。 我有125次测试,耗时12分钟。 我不知道自己在做什么,感觉很慢,但感觉很稳定,结果很棒。 我基本上在3周内重写了最初花了3个月的错误。 如果我现在写了,我可能会在3-5天内完成。 区别? 我的测试套件将有500次测试,需要1-2秒才能运行。 那是随着练习而来的。

    链接地址: http://www.djcxy.com/p/95555.html

    上一篇: beginner problems and stumbling blocks

    下一篇: Asynchronous screen update to gameplay logic, C++