Language agnostic 我应该对“工作”有多严格;尽可能做最简单的事情”;在做TDD时

Language agnostic 我应该对“工作”有多严格;尽可能做最简单的事情”;在做TDD时,language-agnostic,tdd,Language Agnostic,Tdd,对于TDD,您必须 创建失败的测试 尽可能做最简单的事情来通过考试 添加更多测试变体并重复 当模式出现时重构 通过这种方法,你假设可以涵盖所有的情况(至少在我脑海中是这样),但我想知道我在这里是否过于严格,是否有可能“提前思考”一些场景,而不是简单地发现它们 例如,我正在处理一个文件,如果它不符合某种格式,我将抛出一个InvalidFormatException 所以我的第一个测试是: @Test void testFormat(){ // empty doesn't do anyth

对于TDD,您必须

  • 创建失败的测试
  • 尽可能做最简单的事情来通过考试
  • 添加更多测试变体并重复
  • 当模式出现时重构
  • 通过这种方法,你假设可以涵盖所有的情况(至少在我脑海中是这样),但我想知道我在这里是否过于严格,是否有可能“提前思考”一些场景,而不是简单地发现它们

    例如,我正在处理一个文件,如果它不符合某种格式,我将抛出一个
    InvalidFormatException

    所以我的第一个测试是:

    @Test 
    void testFormat(){
        // empty doesn't do anything nor throw anything
        processor.validate("empty.txt"); 
        try {
            processor.validate("invalid.txt");
            assert false: "Should have thrown InvalidFormatException";
        } catch( InvalidFormatException ife ) {
            assert "Invalid format".equals( ife.getMessage() );
        }
     }
    
    我运行它,它失败了,因为它没有抛出异常

    所以我想到的下一件事是:“尽可能做最简单的事情”,所以我:

    啊!!(虽然真正的代码有点复杂,但我发现我自己做过几次类似的事情)

    我知道,我最终必须添加另一个文件名和其他测试,这将使这种方法变得不切实际,并迫使我重构到有意义的东西(如果我正确理解TDD的意义,这就是发现使用所揭示的模式),但是:

    Q:我是不是把“做最简单的事…”这句话看得太字面了?

    很多评论:

    • 如果验证
      “empty.txt”
      时抛出异常,则无法捕获它

    • 不要重复你自己。您应该有一个测试函数来决定验证是否引发异常。然后调用该函数两次,得到两个不同的预期结果

    • 我没有看到任何单元测试框架的迹象。也许我想念他们?但是仅仅使用
      assert
      无法扩展到更大的系统。当您从验证中得到一个结果时,您应该有一种方法向测试框架宣布一个给定名称的测试成功或失败

    • 我对检查文件名(而不是内容)构成“验证”的想法感到震惊。在我看来,这有点太简单了

    关于你的基本问题,我认为你会从一个更广泛的概念中受益,即最简单的事情是什么。我也不是一个原教旨主义者,我可以允许你“提前思考”一点。这意味着要提前到今天下午或明天上午,而不是提前到下周。

    您错过了列表中的第0点:知道该做什么。您说您正在处理一个文件以进行验证。一旦您指定了“验证”的含义(提示:在编写任何代码之前执行此操作),您可能会更好地了解如何a)编写测试,好的,测试实现的规范,以及b)编写最简单的事情


    例如,如果验证是“必须是XML”,那么您的测试用例只是一些不符合XML的字符串,并且您的实现使用XML库,并且(如果需要)将其异常转换为为为“验证”功能指定的异常。

    当然,您对规则的解释过于文字化。 这听起来应该像“做最简单的可能有用的事情…”


    另外,我认为在编写实现时,您应该忘记您试图满足的测试主体。您应该只记住测试的名称(它应该告诉您测试的内容)。通过这种方式,您将被迫编写足够通用的代码,使其变得有用。

    我认为您的方法很好,如果您对它感到满意的话。你没有浪费时间写一个愚蠢的案例,用一种愚蠢的方式解决它——你为真正想要的功能编写了一个严肃的测试,并让它通过——正如你所说的——可能工作的最简单的方式。现在以及将来,当您添加越来越多的实际功能时,您将确保您的代码具有所需的行为,即在一个特定的格式错误的文件上抛出正确的异常。接下来要做的是让这种行为成为现实——你可以通过编写更多的测试来推动这一点。当编写正确的代码比再次伪造代码更简单时,您就可以编写正确的代码了。这种评估因程序员而异——当然,有些人会认为第一次失败的测试是在什么时候编写的


    您使用的是非常小的步骤,这是我和其他一些TDER最舒适的方法。如果你对更大的步骤更满意,那也没关系——但要知道,在那些大步骤绊倒你的情况下,你总是可以求助于更细粒度的流程。

    就像一个方法应该只做一件事一样,一个测试应该只测试一件事(行为)。为了解决给定的示例,我将编写两个测试,例如,
    test\u no\u exception\u for\u empty\u file
    test\u exception\u for\u invalid\u file
    。第二种可能确实是几次测试——每种残疾一次

    TDD过程的第三步应解释为“添加新的试验变量”,而不是“添加新的试验变量”。事实上,单元测试应该是原子的(只测试一件事),并且通常遵循AAA模式:排列-动作-断言。首先验证测试失败是非常重要的,以确保它确实在测试某些东西


    我还要将读取文件和验证其内容的责任分开。这样,测试可以将缓冲区传递给validate()函数,并且测试不必读取文件。通常,单元测试无法访问文件系统,这会使它们的速度减慢

    我也是一名TDD新手,正在努力解决这个问题。在研究过程中,我发现罗伊·奥舍洛夫(Roy Osherove)是我发现的第一个也是唯一一个“可能工作的最简单的事情”的具体和有形的定义(甚至罗伊也承认这只是一个开始)

    简言之,罗伊说:

    看看你刚才写的代码
    public void validate( String fileName ) throws InvalidFormatException {
        if(fileName.equals("invalid.txt") {
            throw new InvalidFormatException("Invalid format");
        }
    }