C# 当一个生产功能有数百万个测试用例时,TDD是如何工作的?

C# 当一个生产功能有数百万个测试用例时,TDD是如何工作的?,c#,asp.net,algorithm,tdd,primes,C#,Asp.net,Algorithm,Tdd,Primes,在TDD中,您选择一个测试用例并实现该测试用例,然后编写足够的生产代码,以便测试通过,重构代码,然后再次选择一个新的测试用例,然后循环继续 我在这个过程中遇到的问题是,TDD说您编写的代码足够通过刚刚编写的测试。我指的是,如果一个方法可以有100万个测试用例,你能做什么?!显然没有写一百万个测试用例 让我用下面的例子更清楚地解释我的意思: internal static List<long> GetPrimeFactors(ulong number) {

在TDD中,您选择一个测试用例并实现该测试用例,然后编写足够的生产代码,以便测试通过,重构代码,然后再次选择一个新的测试用例,然后循环继续

我在这个过程中遇到的问题是,TDD说您编写的代码足够通过刚刚编写的测试。我指的是,如果一个方法可以有100万个测试用例,你能做什么?!显然没有写一百万个测试用例

让我用下面的例子更清楚地解释我的意思:

 internal static List<long> GetPrimeFactors(ulong number)
        {
            var result = new List<ulong>();

            while (number % 2 == 0)
            {
                result.Add(2);
                number = number / 2;
            }

            var divisor = 3;

            while (divisor <= number)
            {
                if (number % divisor == 0)
                {
                    result.Add(divisor);
                    number = number / divisor;
                }
                else
                {
                    divisor += 2;
                }
            }

            return result;
        }
内部静态列表GetPrimeFactors(ulong编号)
{
var result=新列表();
而(数字%2==0)
{
结果:增加(2);
数量=数量/2;
}
var除数=3;

而(除数您忘记了第三步:

  • 红色的
  • 绿色的
  • 重构
  • 编写测试用例会让你陷入困境

    编写足够的代码使这些测试用例通过,这将使您达到绿色


    将您的代码概括为不仅仅适用于您编写的测试用例,同时仍然不破坏任何测试用例,这就是重构。

    TDD确实允许您使用常识,如果您愿意的话。将您的TDD版本定义为愚蠢是没有意义的,只是为了让您可以说“我们没有做TDD,我们正在做一些不那么愚蠢的事情”

    您可以编写一个测试用例,多次调用测试中的函数,并传递不同的参数。这可以防止“编写代码以分解1”、“编写代码以分解2”、“编写代码以分解3”成为单独的开发任务

    要测试多少个不同的值实际上取决于您必须运行测试的时间(因此,在因子分解的情况下,至少0,1,2,3,
    LONG_MAX+1
    ,因为它具有最多的因子,无论哪个值具有最明显的因子,一个Carmichael数和一些具有不同素数因子的完美平方)加上一个尽可能大的值范围,希望覆盖一些你没有意识到的情况,但事实确实如此。这可能意味着编写测试,然后编写函数,然后根据观察到的性能调整范围的大小

    您还可以阅读函数规范,并像测试的值比实际值多一样实现函数。这与“只实现测试的值”并不矛盾,它只是承认在发货日期之前没有足够的时间运行所有2^64个可能的输入,因此实际测试是“逻辑”测试的一个代表性示例,如果您有时间,您将运行该测试。您仍然可以编写您想要测试的代码,而不是您实际有时间测试的代码

    您甚至可以测试随机选择的输入(通常是安全分析师“模糊化”的一部分),如果您发现您的程序员(即您自己)被认定为有悖常理,继续编写只解决测试输入的代码,而不解决其他问题。显然,随机测试的可重复性存在问题,因此使用PRNG并记录种子。在竞赛编程、在线评判程序等方面,你会看到类似的情况,以防止作弊。程序员不知道exa确切地说,哪些输入将被测试,所以必须尝试编写代码来解决所有可能的输入。因为你不能对自己保密,随机输入也会做同样的工作。在现实生活中,使用TDD的程序员不会故意作弊,但可能会因为同一个人编写测试和代码而意外作弊。有趣的是,测试会出错这和代码中遇到的困难情况是一样的

    问题在接受字符串输入的函数中更为明显,可能的测试值远远超过
    2^64
    。选择最好的测试值,也就是说程序员最有可能出错的测试值,充其量只是一门不精确的科学


    您也可以让测试人员作弊,超越TDD。首先编写测试,然后编写代码以通过测试,然后返回并编写更多的白盒测试,(a)包括看起来可能是实际编写的实现中的边缘情况的值;以及(b)包括足够的值以获得100%的代码覆盖率,无论您有时间和意志力去实现什么样的代码覆盖率指标。过程的TDD部分仍然有用,它有助于编写代码,但随后需要迭代。如果这些新测试中的任何一个失败,您可以称之为“添加新需求”,在这种情况下,我认为您所做的仍然是纯TDD。但这只是一个您如何称呼它的问题,实际上您并没有添加新的需求,您正在比编写代码之前更彻底地测试原始需求。

    这是您在任何测试中遇到的第一个问题。TDD并不重要在这里

    是的,有很多很多案例;而且,如果你开始构建系统,就会有很多案例的组合。这确实会导致一场组合爆炸

    如何解决这个问题是一个好问题。通常,您选择算法可能工作相同的等价类,并为每个类测试一个值

    下一步是测试边界条件(记住,CS中两个最常见的错误被一个错误抵消)

    接下来,出于所有实际原因,可以到此为止。不过,请看一下这些课堂讲稿:


    顺便说一句,使用TDD“按书”对于数学问题不是一个很好的主意。Kent Beck在他的TDD书中证明了这一点,实现了计算斐波那契数的函数的最差可能实现。如果你知道一个封闭形式或有一篇文章描述了一个经过验证的算法,只需进行如上所述的健全性检查,不要在整个重构周期中进行TDD-是的