代码之家  ›  专栏  ›  技术社区  ›  Sklivvz

从单元测试自动生成类?

  •  19
  • Sklivvz  · 技术社区  · 16 年前

    我在找一个可以进行单元测试的工具,比如

    IPerson p = new Person();
    p.Name = "Sklivvz";
    Assert.AreEqual("Sklivvz", p.Name);
    

    并自动生成相应的存根类和接口

    interface IPerson         // inferred from IPerson p = new Person();
    {
        string Name 
        { 
            get;              // inferred from Assert.AreEqual("Sklivvz", p.Name);
            set;              // inferred from p.Name = "Sklivvz";
        }
    }
    
    class Person: IPerson     // inferred from IPerson p = new Person();
    {
        private string name;  // inferred from p.Name = "Sklivvz";
    
        public string Name    // inferred from p.Name = "Sklivvz";
        {
            get
            {
                return name;  // inferred from Assert.AreEqual("Sklivvz", p.Name);
            }
            set
            {
                name = value; // inferred from p.Name = "Sklivvz";
            }
        }
    
        public Person()       // inferred from IPerson p = new Person();
        {
        }
    }
    

    我知道Resharper和Visual Studio会做一些这样的工作,但我需要一个完整的工具——命令行或其他什么——自动推断出需要做什么。 如果没有这样的工具,您将如何编写它(例如,扩展resharper、从头开始、使用哪个库)?

    11 回复  |  直到 16 年前
        1
  •  4
  •   Ira Baxter    12 年前

    您需要的是一个用于语言(Java)的解析器,以及一个名称和类型解析器。(“符号表生成器”)。

    在分析源文本之后,编译器通常有一个名称解析程序,它试图记录名称的定义及其对应的类型,以及一个类型检查程序,它验证每个表达式都有一个有效的类型。

    通常名称/类型冲突解决程序在找不到定义时会抱怨。你想要它做的是找到导致问题的“未定义”的东西,并为它推断一个类型。

    为了

     IPerson p = new Person();
    

    名称解析器知道“person”和“iperson”没有定义。如果是

     Foo  p =  new Bar();
    

    没有线索表明您需要一个接口,只是foo是某种抽象的bar父类(例如,类或接口)。因此,工具必须知道它是什么样的决定(“每当您找到这样的构造,假设foo是一个接口…”)。您可以使用启发式方法:ifoo和foo意味着ifoo应该是一个接口,在某个地方,必须有人将foo定义为实现该接口的类。一旦 工具已做出此决定,它需要更新其符号表,以便 转到其他语句:

    为了

     p.Name = "Sklivvz";
    

    假设p必须是一个接口(根据前面的推论),那么name必须是一个字段成员,并且它的类型似乎是赋值中的字符串。

    因此,声明:

     Assert.AreEqual("Sklivvz", p.Name);
    

    名称和类型可以解决,无需进一步的问题。

    ifoo和foo实体的内容有点取决于您;您不必使用get和set,但这是您的个人品味。

    当您在同一语句中有多个实体时,这不会很好地工作:

     x = p.a + p.b ;
    

    我们知道A和B很可能是字段,但是如果它们确实是数字的,或者它们是字符串(这对于Java中的字符串是合法的,关于C.Nyo),您就无法猜出数字类型。 对于C++,你甚至不知道“+”是什么意思,它可能是Bar类中的一个运算符。 所以你要做的就是收集 约束条件 例如,“A是一个不确定的数字或字符串”等,并且随着工具收集证据,它缩小了可能的约束集。(这就像“问题”一词:“乔有七个儿子。杰夫比萨姆高。哈里不能躲在萨姆后面。杰夫的双胞胎是谁?”你必须收集证据并消除不可能的地方)。你还得担心最终会出现矛盾的情况。

    你可以排除P.A+P.B的情况,但是你不能不受惩罚地编写单元测试。如果你想不受惩罚,就有标准的约束解决方案。(真是个概念)。

    好吧,我们有想法了,现在,这能用实际的方法做到吗?

    第一部分需要一个解析器和一个可弯曲的名称和类型解析器。您需要一个约束解算器或至少一个“定义的值流到未定义的值”操作(普通的约束解算器)。

    我们的 DMS Software Reengineering Toolkit 用它 Java Front End 可能会这样。DMS是一个工具构建者的工具,对于那些想要构建以任意方式处理计算机语言的工具的人来说。(想想“用程序片段而不是数字进行计算”)。

    DMS提供通用解析机器,并且可以为给定的前端生成树(例如,Java,并且有一个C端)。 我之所以选择Java是因为我们的Java前端具有所有的名称和类型解析机制,并且它是以源代码形式提供的,所以它可以被弯曲。如果您坚持微不足道的约束求解器,您可能会弯曲Java名称解析程序来确定类型。DMS将允许您组装与代码片段相对应的树,并将它们合并为较大的树;当您的工具为符号表收集事实时,它可以构建原始树。

    在某个地方,你必须决定你完成了。工具必须看到多少个单元测试 在它知道整个接口之前?(我想它会吃掉你提供的所有食物?). 完成后,它为不同的成员组装片段,并为接口构建一个AST;DMS可以使用其预打印器将该AST转换回如您所示的源代码。

    我建议Java在这里,因为我们的Java前端有名称和类型解析。我们的C前端没有。这只是一个“野心”的问题;有人必须写一个,但这是相当多的工作(至少它是Java,我不能想象C是真的不同)。

    但是使用DMS在原则上这个想法是可行的。

    您可以使用其他一些基础结构来实现这一点,这些基础结构允许您访问解析器和可弯曲的名称和类型解析器。对于C来说,这可能不是那么容易获得的;我怀疑MS可能会给您一个解析器,以及对名称和类型解析的访问权,但不会以任何方式改变这一点。也许单声道是答案?

    您仍然需要一个was来生成代码片段并组装它们。你可以尝试通过字符串黑客来实现这一点;我(长期)把程序位粘在一起的经验是,如果你用字符串来做,你最终会把它搞得一团糟。您真的需要表示已知类型的代码片段的片段,这些片段只能以语法允许的方式组合;DMS这样做不会造成混乱。

        2
  •  3
  •   mattlant    16 年前

    令人惊讶的是,没有人真的对你的要求做出任何让步。

    我不知道答案,但我会给出我的想法。

    如果我自己尝试写这样的东西,我可能会看到一个Resharper插件。我之所以这么说,是因为正如你所说,雷斯哈珀可以做到,但在个别步骤。所以我会写一些东西,一行一行地执行,并将适当的resharper创建方法链接在一起。

    现在我决不知道该怎么做,因为我从来没有为雷斯哈伯建造过任何东西,但这正是我要做的。这样做是合乎逻辑的。

    如果你确实写了一些代码,请发布它,因为我也发现这很有用,能够在一个步骤中生成整个框架。非常有用。

        3
  •  1
  •   FryHard    16 年前

    如果您计划编写自己的实现,我将明确建议您查看 NVelocity (C)或 Velocity (Java)模板引擎。

    我以前在代码生成器中使用过它们,并且发现它们使工作变得更加容易。

        4
  •  1
  •   Dmitri Nesteruk    15 年前

    这是可行的——至少在理论上是可行的。我要做的是使用 csparser 解析单元测试(不幸的是,您不能编译它),然后从那里获取它。我能看到的唯一问题是,在方法论方面,您所做的是错误的——从实体类(实际上,Visual Studio正是这样做的)生成单元测试比反过来做更有意义。

        5
  •  1
  •   Jordão    12 年前

    我认为这个问题的真正解决方案是一个非常专业的解析器。因为这不容易,我有一个更便宜的主意。不幸的是,您必须更改编写测试的方式(即,仅创建对象):

    dynamic p = someFactory.Create("MyNamespace.Person");
    p.Name = "Sklivvz";
    Assert.AreEqual("Sklivvz", p.Name);
    

    将使用工厂对象。如果它能找到命名对象,它将创建并返回它(这是正常的测试执行)。如果找不到它,它将创建一个录制代理(A DynamicObject )这将记录所有调用,并且在最后(可能在下拉列表中)可能会发出反映其“看到”被调用内容的类文件(可能基于某些模板)。

    我看到的一些缺点:

    • 需要在“两种”模式下运行代码,这很烦人。
    • 为了让代理“看到”并记录调用,必须执行它们;因此 catch 例如,块必须运行。
    • 您必须更改在测试中创建对象的方式。
    • 你必须使用 dynamic ;您将在随后的运行中丢失编译时安全性,并且它会影响性能。

    我看到的唯一优势是 便宜很多 创建专用的解析器。

        6
  •  0
  •   adriaanp    16 年前

    我喜欢DevExpress的代码流。他们有一个巨大的可定制模板引擎。对我来说最好的是没有对话框。它们还具有从不存在的接口创建方法、接口和类的功能。

        7
  •  0
  •   vijaysylvester    15 年前

    试着看看Pex,一个关于单元测试的微软项目,它仍在研究中。

    research.microsoft.com/en-us/projects/pex研究/

        8
  •  0
  •   Jaco    12 年前

    我认为你要找的是一个模糊的工具包(https://en.wikipedia.org/wiki/fuzz-testing)。

    我从来没用过,你可能会给randoop.net一个生成“单元测试”的机会。 http://randoop.codeplex.com/

        9
  •  -1
  •   community wiki 2 revs Jay Bazuzi    7 年前

    Visual Studio附带了一些对您有帮助的功能:

    产生方法框架 . 当你对一个不存在的方法写一个调用时,你会在方法名上得到一个小的智能标记,你可以用它根据你传递的参数生成一个方法存根。

    如果你是一个键盘人(我是),那么在输入右括号后,你可以做:

    • CTRL. (打开智能标记)
    • 进入 (生成存根)
    • F12 (转到“定义”,转到新方法)

    只有当IDE认为没有匹配的方法时,智能标记才会出现。如果要在智能标记未启动时生成,可以转到 编辑->IntelliSense->生成方法存根 .

    片断 . 小代码模板,使生成公共代码位变得容易。有些很简单(请尝试“if[tab][tab]”)。有些是复杂的(“switch”将为枚举生成事例)。你也可以自己写。对于您的情况,请尝试“class”和“prop”。

    也见“ How to change “Generate Method Stub” to throw NotImplementedException in VS? “有关GMS上下文中的信息片段。

    自动支柱 . 记住,属性可以简单得多:

    public string Name { get; set; }
    

    创建类 . 在解决方案资源管理器中,单击项目名称或子文件夹,选择 Ad& & Gt类 . 键入新类的名称。击中 进入 . 您将在正确的命名空间等中获得类声明。

    实现接口 .当您希望类实现接口时,请编写接口名称部分,激活智能标记,然后选择任一选项为接口成员生成存根。

    这些不是你想要的100%自动化解决方案,但我认为这是一个很好的缓解措施。

        10
  •  -2
  •   Carlos Villela    16 年前

    我发现,每当我需要这样的代码生成工具时,我可能正在编写代码,这些代码可以变得更通用一点,所以我只需要编写一次。在您的示例中,这些getter和setter似乎没有为代码增加任何值——实际上,它只是断言C中的getter/setter机制有效。

    在理解编写此类测试的动机之前,我会避免编写(甚至使用)此类工具。

    顺便说一句,你可能想看看 NBehave ?

        11
  •  -2
  •   Hibri    16 年前

    我用犀牛模型,当我只需要一个简单的存根。

    http://www.ayende.com/wiki/Rhino+Mocks+-+Stubs.ashx