TDD实践之实用主义

原创 2011年11月28日 13:37:27
 

文章

我的书签
  • 您没有收藏的内容

登录注册 以永久保存书签。

该内容已经被标记书签!

标记书签错误,请重试!

TDD实践之实用主义

作者 李光磊 发布于 2008年10月20日

领域
架构 & 设计,
过程 & 实践,
语言 & 开发
主题
敏捷 ,
编程 ,
敏捷技术
标签
测试驱动开发

TDD的概念和实践依然在被怀疑和争议。然而,质疑者驻足不前,实践者却不再理会争论的口水,而是脚踏实地的实践,并获取高效回报。在这个过程中,我们发现TDD和敏捷倡导的理念,帮助我们解决了一个又一个的工程问题。

1. 为沟通选择语言

我们在一个海员管理系统的开发中遇到了问题,这个领域的专业术语我们很难翻译。即使勉强翻译出了,也感觉辞不达意,无论是初看上去,还是过一段时间再看都一头雾水。比如,我们写出了下面的测试用例:

public void test_should_return_NOT_pass_if_duty_higher_than_second_mate_or_second_engineer_and_education_level_is_secondary_and_guraduated_after_2002_02_01() {
   ……
}

但其中second mate/second engineer是什么意思呢? secondary的education level具体又是什么?

还有:

public void test_should_return_third_mate_course_for_jianxi_third_mate() {
  ……
}

jianxi_third_mate是什么? 等等。

当然,我们可以制定一个术语表,请专业人士先帮我们翻译好,然后在代码中遵循这个术语表。然而随着需求的增加,术语层出不穷,并且有特定中国特色的名词根本就没有对应的翻译,于是这个问题就一直困扰着我们。

而直到有一天,在一次重构中我们把上面的第一个测试用例重命名了一下,一切似乎突然间开朗了:

public void test_应该算未通过_if_职务高于二副二管轮_而_学历只是中专_并且_毕业时间晚于2002年2月1日() {
   ……
}

Team里的人纷纷围过来,看着这个跟需求描述里的验收条件几乎一模一样的测试用例名称,感受到一种前所未有的清澈。大家几乎在几秒钟之内就做出了选择:这种形式是可以接受的,而且表达能力更强,交流效果不错。

什么?使用中文作为函数名?这似乎只是那些被主流舆论鄙视的"汉语编程"研究者才搞的东西,我们一直就被教育离这些东西远点,甚至汉语拼音都不推荐使用,一个经常拿来做反面教材的例子就是数据库表的列名使用汉语拼音,这被看作不专业的表现。又或者,以后团队中加入外国开发者怎么办?

幸运的是,我们是软件工程师,不是计算机科学家。学术理论可以极端,而工程一定是某种折衷。定理由自然界精确遵守,而工程却是各种应力的人为平衡。

具体到这个案例,让我们正视现实:

  1. 团队成员并不善长本项目领域的专业英语。
  2. 任何翻译都会造成一定的信息损失,尤其在一些具有中国特色的领域,比如"中专"翻译为英语就很难像中文一样简洁直观。
  3. 在可预见的将来,不会有老外加入开发团队。

而选用中文却能够让我们更好的坚持以下原则:

  1. 代码除了完成功能, 另外一个重要的功能是交流。(我们选择了对团队来说最有效的交流方式)
  2. 用测试用例的名字来描述需求。(用中文描述更精确, 易于理解)

当然我们也会失去一些东西,比如对上面提到的"应该坚持使用英文"原则的放弃。在这里我们认为放弃这条原则的收益大于损失。一种损失就是失去了学习英文的机会,比如上面最后一个测试用例,用中文写出来就是:

public void test_见习三副应该参加三副的培训() {
   ……
}

或者有人会说:"见习"的英语是"intern",常用词啊。然而系统中还有另外一类角色,叫"实习三副"等,那才是"intern"。实习是实际动手,担当实际的职责;见习是只看不练,跟在后面观摩学习。见习的英文单词是"noviciate",并不为项目组所熟悉,而我们也不再关心它。

总之,在实践中应当权衡各种利弊,选择对你来说最有效的方式。

2. 用大量测试来驱动

在项目组中曾经发生过自以为完成了某个特性,后来却发现漏掉了某些验收条件,甚至是比较重要验收条件的事情。而这也是TDD经常被质疑的一点,就是如何保证测试的完备性。因为总是想一点写一点,不经过深入的思考,不可避免的会漏掉某些测试条件。

然而,TDD并不妨碍你深入思考,只是劝你在实现时步子不要太大,小步前进获得对问题的进一步认识以随时调整设计和实现。不过今天不争论TDD的哲学问题,我们只是关注用什么样的实践来消除对于TDD测试完备性的疑虑。

事实上,完全可以根据需求文档,验收条件,进行"深入思考",从一开始就写下所有能想到的单元测试用例,就跟测试人员在产品出来前就对着需求准备测试用例一样。

哦,等等,这还叫 TDD 吗? TDD 所强调的小步前进,随时应变哪里去了?为全面的测试用例所花费的大量努力岂不是有浪费的风险?

嗯,不错,TDD强调小步前进的原因就是要避免浪费。如果我们能找到一种方法,既能够提醒我们不要忘记需求,又让我们在需求变化时不致浪费太多,岂不是皆大欢喜?

想想,我们用什么来描述需求?是测试用例名称,而不是测试用例的函数体,而名称的书写几乎是没有成本的。从需求文档中把验收条件抠出来即可。如:

    public void test_会被认为不服从调配_if_the_seaman_在当前职位上曾经旷工() {
        // TODO
    }

    public void test_会被认为不服从调配_if_the_seaman_在当前职位上曾经请过病假() {
        // TODO
    }

    public void test_会被认为不服从调配_if_the_seaman_在当前职位上曾经请过事假() {
        // TODO
    }

    public void test_会被认为不服从调配_if_the_seaman_在当前职位上曾经被遣返() {
        // TODO
    }

    public void test_不会被认为不服从调配_if_the_seaman_在当前职位上从未旷工_请病假_请事假_和遣返() {
        // TODO
    }

两分钟,我们就把这个用户故事的测试用例按照验收条件里说的全部描述出来了。函数体全部都是空的,因此所有的测试都是通过的,不会强迫你一次性把所有的测试都实现。

每次你流览或修改这段代码,空的函数体或里面的// TODO都会提醒你还有测试没有完成。即使你不在这个特性上工作了,切换过来的Pair也会从你遗留的测试用例名称中迅速的了解需要做什么。

这种方法是怎么解决问题的呢?

第一,还是让我们正视现实:如果需求描述不和代码放在一起,开发人员很少会在开发过程中去翻阅需求文档,甚至是特性编码结束后。这在成熟的开发团队中会有改善,但仍然不可避免。把需求描述以测试用例名称的方式放进代码,便会无时无刻不在提醒开发者,还有这个这个这个验收条件没满足。

第二,我们依然坚持了以下原则:

  1. 用测试用例的名字来描述需求。
  2. 小步前进,编写一个测试用例,实现一段产品代码,编写下一个测试用例,实现下一段产品代码。(因为所有的未完成测试都是通过的,不妨碍你运行测试,提交代码和持续集成)
  3. 当实现过程中发现事情并不是当初想的那样时,随时更改或删除之前写的测试用例,不会造成大的浪费。(因为只是函数名加空的函数体,成本很低)

在开始写下"所有"的测试用例名称并不意味着一劳永逸。中间当需求发生变化,我们需要对应的添加或删除一部分测试用例。在实现的过程中,发现某些条件不在验收范围内,或许是之前没考虑到,那么跟BA/QA确认后,需要添加到用例列表中。

(细心的读者可能发现,这里面存在着重复。就是以普通文本形式存在的验收条件和以测试用例名称存在的验收条件。或许应该有类似SVN2Wiki的工具,来消除这类重复)

当然,完备性还有其它的含义和检测条件,不是说你提前多写几个用例就是完备了。这里只是用一种成本最低的方式来解决问题。

3. 一个环境,多个断言

随着时间的推移,项目组发现测试运行的越来越慢。当然,这是一个普遍现象,也已经有很多方法来加速测试的运行速度。但很多需要新工具的支持,而项目组暂时没时间去切换工具。有没有其它更方便的做法?

像其它性能问题一样,我们首先需要确定瓶颈在哪里。我们发现主要是每个测试用例运行前搭建测试所需的环境相对较为耗时,尤其是Selenium测试,它需要启动和关闭浏览器。并且,很多测试用例其实使用相同的环境设置,只是每个用例仅仅去断言其中一个需求。

我们可以修改Runner,让一组测试使用同一个浏览器实例,每次环境的清理通过清理Session来完成。而我们也可以采取另外一种方法,就是合并使用相同环境设置的测试用例,把它们的断言都放进同一个用例。

哦,这又违反了Kent Beck 为TDD制定的原则:每个测试用例最好只有一个断言。

好,让我们再一次分析原则背后的理念。一个用例一个断言,是为了让测试更清晰,更精确的描述需求,测试失败更容易定位。那么有没有一种方法,既能让多个断言共享相同的环境设置,又能清晰精确的描述需求呢?

想想,我们用什么来描述需求?是测试用例的名称,确切的说,是函数的名称。只要我们把一组组相关的断言封装到一个个函数里,给它们一个能够清晰精确的描述这组断言对应的需求的名称,然后在测试用例里面调用这些函数就可以了。这样我们只需为多个断言设置一次环境,而同时又保留了清晰精确的表达需求的能力。

    @Test
    public void test_should_show_step_details_info_in_todo_item_page() throws Exception {
        TodoItemPage page = navigator.gotoTodoItemPage( );

        should_show_step_name_as_page_title(activeStepOfNonStartedInstance, page);
        should_show_start_processing_button_if_current_step_status_is_waiting(page);
        should_show_transition_buttons(activeStepOfNonStartedInstance, page);
        should_NOT_ask_user_to_input_his_opinion_if_current_step_status_is_NOT_processing(page);
        should_show_comment_box_after_click_start_process(page);
    }

    private void should_show_step_name_as_page_title(FlowStep step, TodoItemPage page) {
        assertEquals(step.getName(), page.title());
    }

    private void should_show_start_processing_button_if_current_step_status_is_waiting(TodoItemPage page) {
        assertTrue(page.isStartProcessingButtonVisible());
    }

    private void should_show_comment_box_after_click_start_process(TodoItemPage page) {
        page。clickStartProcessingButton();
        assertTrue(page.isCommentBoxAppear());
    }

    private void should_ask_user_to_input_his_opinion_if_current_step_status_is_processing(TodoItemPage page) {
        assertTrue(page.isCommentBoxVisible());
        assertTrue(page.isActionButtonsVisible());
    }

    private void should_ask_user_to_select_next_step_operators(FlowTransitionDefinition nextTransitonOfStep,
                                                               TodoItemPage page) {
        assertTrue(page。isUserGroupVisible(nextTransitonOfStep.getId()));
    }
    ……

小结

回过头来我们看看上面的三个实践,它们如出一辙的,一次又一次的"违反"了某种原则。它们分别是"不能用汉语","不能一次编写多个测试用例",和"不能在一个用例里面使用多组断言",而实际上,我们违反的只是这些原则的外在形式,但却坚持了这些原则背后的思想,如最有效的沟通,注重实效而不是形式。以此为基石,我们可以在出现新的约束的情况下,灵活运用,发明各种实践,并享受由此带来的效率提升。


作者简介:李光磊,软件工程师,同时还是一位敏捷教练,就职于ThoughtWorks。他还是活跃的blog作者,了解他最新的想法,请访问http://blog.csdn.net/chelsea

17 条回复

关注此讨论 回复

实践者的实用文章 发表人 田 乐 发表于 20/10/2008 04:08 <!-- ww:date name="%{top.creationDate}" format="%{#request['postDateTimeFormat']}" /> -->
很不错的总结啊 发表人 张 晓庆 发表于 20/10/2008 06:41 <!-- ww:date name="%{top.creationDate}" format="%{#request['postDateTimeFormat']}" /> -->
关于使用中文 发表人 Zoom Quiet 发表于 21/10/2008 08:17 <!-- ww:date name="%{top.creationDate}" format="%{#request['postDateTimeFormat']}" /> -->
Bad smell: 冗长的方法名而且还是中文的 发表人 Zhang Charlie 发表于 22/10/2008 09:33 <!-- ww:date name="%{top.creationDate}" format="%{#request['postDateTimeFormat']}" /> -->
重构方法名:test_见习三副应该参加三副的培训 发表人 Zhang Charlie 发表于 29/10/2008 04:38 <!-- ww:date name="%{top.creationDate}" format="%{#request['postDateTimeFormat']}" /> -->
Re: 重构方法名:test_见习三副应该参加三副的培训 发表人 li yanhui 发表于 30/10/2008 01:26 <!-- ww:date name="%{top.creationDate}" format="%{#request['postDateTimeFormat']}" /> -->
Re: 重构方法名:test_见习三副应该参加三副的培训 发表人 Zhang Charlie 发表于 31/10/2008 06:32 <!-- ww:date name="%{top.creationDate}" format="%{#request['postDateTimeFormat']}" /> -->
Re: 重构方法名:test_见习三副应该参加三副的培训 发表人 Xiong Jeff 发表于 02/11/2008 05:32 <!-- ww:date name="%{top.creationDate}" format="%{#request['postDateTimeFormat']}" /> -->
霍编辑,这回又是 Jeff Xiong 先挑起的(请勿删贴) 发表人 Zhang Charlie 发表于 03/11/2008 11:05 <!-- ww:date name="%{top.creationDate}" format="%{#request['postDateTimeFormat']}" /> -->
中文夹杂着英文的方法名 发表人 zhu pan 发表于 29/10/2008 10:42 <!-- ww:date name="%{top.creationDate}" format="%{#request['postDateTimeFormat']}" /> -->
Bad Smell: Long, complex test conditions as method name 发表人 Zhang Charlie 发表于 30/10/2008 12:06 <!-- ww:date name="%{top.creationDate}" format="%{#request['postDateTimeFormat']}" /> -->
Jeff Xiong 再现狗屁不通的逻辑 发表人 Zhang Charlie 发表于 04/11/2008 12:14 <!-- ww:date name="%{top.creationDate}" format="%{#request['postDateTimeFormat']}" /> -->
Re: Jeff Xiong 再现狗屁不通的逻辑 发表人 Xiong Jeff 发表于 04/11/2008 01:11 <!-- ww:date name="%{top.creationDate}" format="%{#request['postDateTimeFormat']}" /> -->
原来这种descriptive的方式也被TDD使用啊 发表人 徐 毅 发表于 04/11/2008 02:09 <!-- ww:date name="%{top.creationDate}" format="%{#request['postDateTimeFormat']}" /> -->
张恂对于本案的详细分析 发表人 Zhang Charlie 发表于 12/11/2008 11:20 <!-- ww:date name="%{top.creationDate}" format="%{#request['postDateTimeFormat']}" /> -->
无意制造新矛盾,只是想说说想法。 发表人 Lin Qing 发表于 12/11/2008 11:54 <!-- ww:date name="%{top.creationDate}" format="%{#request['postDateTimeFormat']}" /> -->
Re: 无意制造新矛盾,只是想说说想法。 发表人 X 1073 发表于 28/11/2008 02:00 <!-- ww:date name="%{top.creationDate}" format="%{#request['postDateTimeFormat']}" /> -->
  1. 返回顶部

    实践者的实用文章

    20/10/2008 04:08 <!-- ww:date name="%{top.creationDate}" format="%{#request['postDateTimeFormat']}" /> -->发表人 田 乐

    在测试里面保有更多的场景描述的元数据非常好,可以让测试和验收条件合二为一。中文是多字节编码的,所以实际上我们可以在支持Unicode的Java里面使用同样的方法名长度限制下写出更清楚的验收条件,就好比中文的Twitter可以比英文的Twitter在同样73个字符的限制下写更多的句子(保有更多的信息)。

  2. 返回顶部

    很不错的总结啊

    20/10/2008 06:41 <!-- ww:date name="%{top.creationDate}" format="%{#request['postDateTimeFormat']}" /> -->发表人 张 晓庆

    第二个原则是一个很不错的实践,现在一直在用。
    比如开发一个story,需要涉及到界面/controller/dao多个部分。首先在selenium测试中写出类似于验收条件的空测试方法,可以很大程度上避免漏掉一些验收条件。

  3. 返回顶部

    关于使用中文

    21/10/2008 08:17 <!-- ww:date name="%{top.creationDate}" format="%{#request['postDateTimeFormat']}" /> -->发表人 Zoom Quiet

    如果可以使用Python 的话,
    有中蟒和周蟒可以选择,其中 周蟒~ zhpy
    code.google.com/p/zhpy/

    跟上了Python 的版本,而且不依赖其它模块,可以直接使用,,,

  4. 返回顶部

    Bad smell: 冗长的方法名而且还是中文的

    22/10/2008 09:33 <!-- ww:date name="%{top.creationDate}" format="%{#request['postDateTimeFormat']}" /> -->发表人 Zhang Charlie

    赞成作者的实用主义观点。但冗长的方法名,而且还是中文的,实在有点离谱。

    目前的这个方案是脆弱的,难以适应变化(中文方法名称本身就是不稳定的),我觉得作者给出的理由也是不充分的。中文术语名称,也未必比英文术语更稳定、更准确吧。

    这样就真的做到了“最有效的沟通”和“最有效的交流方式”?而用了这么多下划线,是自找麻烦,可读性反而下降了。

    jianxi_third_mate 更像一个笑话。

    test case 方法名其实可以很简单,比方用某些关键词结合 ID 来命名。说明测试的目标或内容,为什么不能采用注释、注解、映射表或其他辅助工具呢?

    总之,用中文方法名,尤其还是冗长的、难以输入、维护和适应变化的方法名,是个 bad idea,我一开始就不会用(省却了重构的麻烦)。

    中西合璧的敏捷 OO 教练 张恂
    www.zhangxun.com

  5. 返回顶部

    中文夹杂着英文的方法名

    29/10/2008 10:42 <!-- ww:date name="%{top.creationDate}" format="%{#request['postDateTimeFormat']}" /> -->发表人 zhu pan

    中文夹杂着英文的方法名看着实在是比较恶心啊

  6. 返回顶部

    重构方法名:test_见习三副应该参加三副的培训

    29/10/2008 04:38 <!-- ww:date name="%{top.creationDate}" format="%{#request['postDateTimeFormat']}" /> -->发表人 Zhang Charlie

    “test_见习三副应该参加三副的培训”怎么看都不像一个合理的方法名,建议避免使用。


    比如上面最后一个测试用例,用中文写出来就是:

    public void test_见习三副应该参加三副的培训() {
    ……
    }


    太极敏捷最佳实践:

    1)开发中所有人员一律采用统一的术语表和中英文译法;

    2)对于常用的关键词、领域概念、专业术语,规定统一的英文简写、缩写和/或标记代码,以便在代码、文档、模型等各种工件中使用;


    Solution:

    1)所有的专用术语“三副”均应采用缩写,比方 3M,见习三副(3Mn),实习三副(2Mi);

    2)把所有与“见习三副”有关的测试都放入一个测试类中(基于高内聚原则),这样就可以去掉所有测试方法名前、累赘的“见习三副”;

    举例:

    // TCNote: 见习三副应该参加三副的培训(该注释其实可省略)
    public void testAttend3MTraining() {...}

    是不是更加简明、方便而有效?

  7. 返回顶部

    Bad Smell: Long, complex test conditions as method name

    30/10/2008 12:06 <!-- ww:date name="%{top.creationDate}" format="%{#request['postDateTimeFormat']}" /> -->发表人 Zhang Charlie

    本文采用复杂的测试条件作为方法名,降低了测试程序的可读性、可维护性,容易产生错误。建议不要采用。


    原先的代码:

    @Test
    public void test_should_show_step_details_info_in_todo_item_page() throws Exception {
    TodoItemPage page = navigator.gotoTodoItemPage( );
    should_show_step_name_as_page_title(activeStepOfNonStartedInstance, page);
    should_show_start_processing_button_if_current_step_status_is_waiting(page);
    should_show_transition_buttons(activeStepOfNonStartedInstance, page);
    should_NOT_ask_user_to_input_his_opinion_if_current_step_status_is_NOT_processing(page);
    should_show_comment_box_after_click_start_process(page);
    }

    private void should_show_step_name_as_page_title(FlowStep step, TodoItemPage page) {
    assertEquals(step.getName(), page.title());
    }

    private void should_show_start_processing_button_if_current_step_status_is_waiting(TodoItemPage page) {
    assertTrue(page.isStartProcessingButtonVisible());
    }

    private void should_show_comment_box_after_click_start_process(TodoItemPage page) {
    page.clickStartProcessingButton();
    assertTrue(page.isCommentBoxAppear());
    }

    private void should_ask_user_to_input_his_opinion_if_current_step_status_is_processing(TodoItemPage page) {
    assertTrue(page.isCommentBoxVisible());
    assertTrue(page.isActionButtonsVisible());
    }

    // ...


    以上程序不但可读性差,而且好像还存在着逻辑错误。

    Refactoring results:

    原先代码中的几个 private 方法,完全可以采用更加简洁而有效的名称。

    Class TestTodoItemPage

    @Test
    public void testStepDetailsInfoScenarioA() throws Exception {
    TodoItemPage page = navigator.gotoTodoItemPage();

    verifyPageTitle(activeStepOfNonStartedInstance, page);

    verifyStartProcessingButton(page);

    verifyTransitionButtons(activeStepOfNonStartedInstance, page);

    verifyNotAskUsrInputOpinion(page);

    page.clickStartProcessingButton();
    verifyCommentBox(page);
    }


    // = step name
    private void verifyPageTitle(FlowStep step, TodoItemPage page) {
    assertEquals(step.getName(), page.title());
    }

    // if step status is waiting
    private void verifyStartProcessingButton(TodoItemPage page) {
    assertTrue(page.isStartProcessingButtonVisible());
    }

    // if step status is NOT processing
    private void verifyNotAskUsrInputOpinion(TodoItemPage page) {
    assertFalse(page.isCommentBoxVisible());
    assertFalse(page.isActionButtonsVisible());
    }

    // after clicking start processing
    private void verifyCommentBox(TodoItemPage page) {
    assertTrue(page.isCommentBoxAppear()); //?? page.isCommentBoxVisible()
    }

  8. 返回顶部

    Re: 重构方法名:test_见习三副应该参加三副的培训

    30/10/2008 01:26 <!-- ww:date name="%{top.creationDate}" format="%{#request['postDateTimeFormat']}" /> -->发表人 li yanhui

    这种做法的话,在junit的report里只能看到“testAttend3MTraining”失败了,谁懂?

    就算是项目组开发人员,如果有更简单清楚的方法,谁愿意背那么多术语对应代码


    举例:

    // TCNote: 见习三副应该参加三副的培训(该注释其实可省略)
    public void testAttend3MTraining() {...}

    是不是更加简明、方便而有效?

  9. 返回顶部

    Re: 重构方法名:test_见习三副应该参加三副的培训

    31/10/2008 06:32 <!-- ww:date name="%{top.creationDate}" format="%{#request['postDateTimeFormat']}" /> -->发表人 Zhang Charlie


    2008年10月30日 上午12时26分 发表人 yanhui li

    这种做法的话,在junit的report里只能看到“testAttend3MTraining”失败了,谁懂?

    就算是项目组开发人员,如果有更简单清楚的方法,谁愿意背那么多术语对应代码


    对于大副、二副、三副之类的常用词,大家约定采用缩写、助记符(比方 1MTE、2MTE、3MTE)还是有必要的,既简单、又清楚,开发人员天天用,还需要背吗?

    当然,还是要控制缩写、助记符的范围和数量,如果不常用的、确实难记、易混淆的就没必要缩写了。

  10. 返回顶部

    Re: 重构方法名:test_见习三副应该参加三副的培训

    02/11/2008 05:32 <!-- ww:date name="%{top.creationDate}" format="%{#request['postDateTimeFormat']}" /> -->发表人 Xiong Jeff

    对于大副、二副、三副之类的常用词,大家约定采用缩写、助记符(比方 1MTE、2MTE、3MTE)还是有必要的,既简单、又清楚,开发人员天天用,还需要背吗?

    船上的职位总共有71个,人家信息中心干这个系统干多少年了都得现用现查,你牛逼,你都能背下来。
    还扯什么“把所有与“见习三副”有关的测试都放入一个测试类中”。几十个功能,每个功能都涉及到每个职位。我这个人有点笨,我不知道一个功能的测试散在71个测试里它怎么就符合“高内聚原则”了。
    有些话我都说烂了。没有实践经验就少扯些看起来很美其实狗屁不通的蛋。哦对了,听说你最近又去了某CMM 5级企业做咨询,看见他们的配置管理问题了吗?怎么还没见你摘他们牌呢?

  11. 返回顶部

    霍编辑,这回又是 Jeff Xiong 先挑起的(请勿删贴)

    03/11/2008 11:05 <!-- ww:date name="%{top.creationDate}" format="%{#request['postDateTimeFormat']}" /> -->发表人 Zhang Charlie

    看清楚了吧,希望你们不要再包庇,混淆是非。


    2008年11月2日 上午3时32分 发表人 Jeff Xiong

    船上的职位总共有71个,人家信息中心干这个系统干多少年了都得现用现查,你牛逼,你都能背下来。

    还扯什么“把所有与“见习三副”有关的测试都放入一个测试类中”。几十个功能,每个功能都涉及到每个职位。我这个人有点笨,我不知道一个功能的测试散在71个测试里它怎么就符合“高内聚原则”了。

    有些话我都说烂了。没有实践经验就少扯些看起来很美其实狗屁不通的蛋。哦对了,听说你最近又去了某CMM 5级企业做咨询,看见他们的配置管理问题了吗?怎么还没见你摘他们牌呢?


    我们本来在认真地探讨技术问题,没想到这个“大嘴”惹事佬又跳将出来。其实 Jeff Xiong 自 2005 年放弃著名媒体人的身段以来,连 5 年的正式开发经验都没有,有什么资格跟别人理论“实践经验”呢?秉性使然。

    霍编辑,按照你们 InfoQ China 的标准,这算不算恶意的谩骂和人身攻击呢?

    怒放几个“牛逼”、“扯淡”、“狗屁不通”,并不能代表你熊节的牛逼吧。这些词汇,既然你们 InfoQ 中国的编辑可以用,我们读者当然也可以用了。

    我看 Jeff Xiong 这种“裤腿撕咬者”、“狂躁症患者”的著名缔造者,素质低、品质差的混混(已经被其这些年来的言行所实证),显然不配继续留在 InfoQ 中国的编辑队伍里,建议你们早点清理门户,免得大家难堪,避而不及。

    It's really a SHAME for an InfoQ editor to behave like this.

  12. 返回顶部

    Jeff Xiong 再现狗屁不通的逻辑

    04/11/2008 12:14 <!-- ww:date name="%{top.creationDate}" format="%{#request['postDateTimeFormat']}" /> -->发表人 Zhang Charlie

    2008年11月2日 上午3时32分 发表人 Jeff Xiong

    船上的职位总共有71个,人家信息中心干这个系统干多少年了都得现用现查,你牛逼,你都能背下来。


    71 个职位,当然没必要全背。但其中可能有十几个、几十个是常用的,而像大副、二副、三副之类的日常词汇,还需要背吗?

    我的建议是,对于常用业务词汇在程序中可以采用缩写/助记符代替,比方 1MTE、2MTE、3MTE 等等,以简化程序的编写(这本来是废话,稍有点实践经验的程序员都明白,可有些人就是连废话也不懂)。

    我可没建议大家把这 71 个职位都背下来。对于生僻、难记的完全可以现用现查。

    熊节却扯出了“你牛逼,你都能背下来”。什么阅读、理解力?这叫无理取闹。

    2008年11月2日 上午3时32分 发表人 Jeff Xiong

    还扯什么“把所有与“见习三副”有关的测试都放入一个测试类中”。几十个功能,每个功能都涉及到每个职位。我这个人有点笨,我不知道一个功能的测试散在71个测试里它怎么就符合“高内聚原则”了。


    我们这里讨论的是如何简化 test_见习三副应该参加三副的培训()。

    把与见习三副相关的、见习三副作主语的、见习三副发起的 ... 测试都归并到一起,可以简化测试方法名称的书写,没必要写 n 个“见习三副应该 ...”,这是显而易见的方法。而把见习三副的测试与其他无关的测试混在一起,很可能违反了高内聚原则。

    “见习三副应该参加三副的培训”,这个功能涉及到了每个职位?扯蛋吧。

    而 Jeff Xiong 所谓的“一个功能的测试散在71个测试里”,更是不知所云。

    我看你这个人不能叫一点笨,是相当的笨。这就叫胡搅蛮缠,自取其辱。

  13. 返回顶部

    Re: Jeff Xiong 再现狗屁不通的逻辑

    04/11/2008 01:11 <!-- ww:date name="%{top.creationDate}" format="%{#request['postDateTimeFormat']}" /> -->发表人 Xiong Jeff


    “见习三副应该参加三副的培训”,这个功能涉及到了每个职位?扯蛋吧。

    而 Jeff Xiong 所谓的“一个功能的测试散在71个测试里”,更是不知所云。

    说你牛逼你还真牛逼。
    我要是告诉你还有个测试说“见习三管轮应该参加三管轮的培训”,不知道您老又做何感想呢?
    顺便告诉你吧,这样的逻辑,它还真有好几十个。没办法,谁叫人家海员级级晋升都要培训呢?
    71 个职位,当然没必要全背。但其中可能有十几个、几十个是常用的

    真糟糕,这71个职位总是同时出现。没办法,谁叫人家一统计总是要所有职位都一起统计呢?其实我也觉得船长轮机长大副大管轮之类的应该比其他虾米职位更重要,可惜啊,客户他不这么想啊。

    再说一遍,没有实践经验就少扯这种看起来很美其实狗屁不通的蛋。你这辈子就没干过一个跟航运有关的项目还扯什么“常用职位”,说真的,你知道啥叫职位不?

  14. 返回顶部

    原来这种descriptive的方式也被TDD使用啊

    04/11/2008 02:09 <!-- ww:date name="%{top.creationDate}" format="%{#request['postDateTimeFormat']}" /> -->发表人 徐 毅

    原以为只有acceptance test里面才使用到这种命名的方式呢。

  15. 返回顶部

    张恂对于本案的详细分析

    12/11/2008 11:20 <!-- ww:date name="%{top.creationDate}" format="%{#request['postDateTimeFormat']}" /> -->发表人 Zhang Charlie

    全文请见:

    www.zhangxun.com/?sname=InfoQTDDPractice


    作者 李光磊 发布于 2008年10月20日 上午1时2分

    回过头来我们看看上面的三个实践,它们如出一辙的,一次又一次的"违反"了某种原则。它们分别是"不能用汉语","不能一次编写多个测试用例",和"不能在一个用例里面使用多组断言",而实际上,我们违反的只是这些原则的外在形式,但却坚持了这些原则背后的思想,如最有效的沟通,注重实效而不是形式。以此为基石,我们可以在出现新的约束的情况下,灵活运用,发明各种实践,并享受由此带来的效率提升。


    总体上,我肯定作者反对教条、提倡创新和重构的思路,这种精神值得发扬。但另一方面,我认为作者的重构在技术上还没有到位,为了实现“注重实效而不是形式”的目标,我们还需要继续重构。

    原文的篇幅不长。从目前作者所提供的信息来看,我认为作者的分析是比较片面的,因为过分自信而导致片面,只看到了这些“创新”做法可能带来的好处和优点,而对这些做法存在哪些缺点和不足,却分析得不够,既缺乏深入性和全面性,也缺乏前瞻性。作者及其同事们所给出的三个实践,尤其是直接把冗长的有关测试/验收条件、测试结果的说明用作(简单地拷贝为)测试用例的方法名这一做法,违反了简化、稳定、灵活和 DRY(Don't Repeat Yourself)等敏捷软件的基本设计原则和思想。我认为目前原文中给出的解决方案,只能算是一些低成本的、脆弱的、不稳定的临时措施(临时搭建的工棚?),存在明显的 bad smells,需要通过重构来进一步提高测试程序的质量。

    当然,如果作者或他人能够给出支持这些做法的更充分的信息和理由,我也准备接受。


    资深敏捷 OO 教练
    www.zhangxun.com

  16. 返回顶部

    无意制造新矛盾,只是想说说想法。

    12/11/2008 11:54 <!-- ww:date name="%{top.creationDate}" format="%{#request['postDateTimeFormat']}" /> -->发表人 Lin Qing

    感觉这样,
    技术讨论很不错,
    有新想法也不错,
    说些有点脏的话也没什么,
    没必要太在意,显得小气了。

    倒是为何那个教练老是给自己做广告呢?
    自夸在我看来总是有些自大。
    虽然,自信没问题,但是,
    中国嘛,还是要收敛些的好吧。

    这样复杂的测试,
    用动态语言会不会更加方便呢?
    这样很多重复且又有微小差别的测试可以动态创建。

  17. 返回顶部

    Re: 无意制造新矛盾,只是想说说想法。

    28/11/2008 02:00 <!-- ww:date name="%{top.creationDate}" format="%{#request['postDateTimeFormat']}" /> -->发表人 X 1073

    还吵呢。。。这都多久了。。。。有什么深仇大恨滴。。。。。

    用注释蛮好啊,不理解为什么一定要在case的命名上斤斤计较。。。。是有工具可以支持通过名字做某些操作的功能吗?如果是那样是不是有些太依赖工具了。。。。。
    第2和第3点还是蛮好滴,我也经常这样做,不过正在尝试慢慢改变,学习在这种情况下寻求平衡的方法。

单元测试与TDD实践

  • 2012年04月17日 23:52
  • 343KB
  • 下载

Android开发:实践TDD的一些建议

最近部分采用了TDD的方法来开发一个模块,小有收获特此总结一下: 1. TDD的基本原则 TDD的最核心思想就是先明确需求,且用代码的方式量化,明确需求标准,然后进行编码实现以达成由代码测试来衡量...

怎么用TDD,这篇文章不错,自己也在实践中

作者:陈天 链接:https://zhuanlan.zhihu.com/p/24997923 来源:知乎 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。TDD(Test D...

TDD实践之路--泛型和匿名方法在DELPHI单元测试中的妙用

昨天启动TDD之旅。开始一切都很顺利,按照“故事情节”,遵守PF,对方法的调用测试很顺利。接着麻烦来了。由于PascalMock没有事件引发的处理机制(特别是我的事件委托都是私有的)造成在测试事件引发...

TDD实践小结

所谓TDD,就是测试驱动开发。近期看了一些如何写代码的书,包括《修改代码的艺术》,《代码整洁之道》等,这些书都提到了TDD。 我记得几年以前在书店就开始看到关于极限编程的书,也偶尔听到测试驱动开发的...

TDD开发实践

JParsec 与 JPJson最近两天写了两个项目,一个是JParsec,另一个是JPJson.JParsecJParsec是Parsec的Java版本实现.由于暑假实习的时候已经写过了JSPars...

实用主义计时器

  • 2013年04月29日 19:50
  • 13KB
  • 下载

一点实用主义的学英语心得

转自http://begeek.cn/post/5608.html?ref=myread 我因为爱谈论英文书,经常被人要求讲讲学英语的心得。我英文水平其实拿不出手,说话和写作都仅限于平时...

强调实用主义——采访Python专家HyryStudio

本期采访嘉宾:张若愚(HyryStudio) 日本神户制钢综合研究所从事研究开发工作,研究方向为:嵌入式DSP信号处理系统开发,嵌入式MCU控制系统开发,工业控制软件开发,信号处理、数据处理以及...

向着实用主义至上的世界呐喊——《三傻大闹宝莱坞》观后感

《三傻大闹宝莱坞》是继《流浪者》之后,在中国知名度最高的印度电影了,哪怕那些不是印度电影粉丝的观众,也不能否认它的出众,如果说《流浪者》是因其犀利的批判性,而获得广泛的认可,那么《三傻大闹宝莱坞》则是...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:TDD实践之实用主义
举报原因:
原因补充:

(最多只允许输入30个字)