Skl*_*vvz 19 c# unit-testing code-generation
我正在寻找一种可以进行单元测试的工具,比如
IPerson p = new Person();
p.Name = "Sklivvz";
Assert.AreEqual("Sklivvz", p.Name);
Run Code Online (Sandbox Code Playgroud)
并自动生成相应的存根类和接口
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();
{
}
}
Run Code Online (Sandbox Code Playgroud)
我知道ReSharper和Visual Studio会做其中的一些,但我需要一个完整的工具 - 命令行或诸如此类的东西 - 它会自动推断需要完成的工作.如果没有这样的工具,你会如何编写它(例如,从头开始使用哪些库扩展ReSharper)?
您似乎需要的是您的语言(Java)的解析器以及名称和类型解析器。(“符号表生成器”)。
解析源文本后,编译器通常有一个名称解析器,它尝试记录名称及其相应类型的定义,以及一个类型检查器,它验证每个表达式是否具有有效类型。
通常,名称/类型解析器在找不到定义时会抱怨。您希望它做的是找到导致问题的“未定义”事物,并推断它的类型。
为了
IPerson p = new Person();
Run Code Online (Sandbox Code Playgroud)
名称解析器知道“Person”和“IPerson”未定义。如果是的话
Foo p = new Bar();
Run Code Online (Sandbox Code Playgroud)
没有任何线索表明您想要一个接口,只是 Foo 是 Bar 的某种抽象父级(例如,类或接口)。因此,工具必须知道它是什么(“每当你找到这样的构造时,假设 Foo 是一个接口......”)。您可以使用启发式:IFoo 和 Foo 意味着 IFoo 应该是一个接口,并且有人必须在某个地方将 Foo 定义为实现该接口的类。一旦工具做出了这个决定,它就需要更新其符号表,以便它可以继续执行其他语句:
为了
p.Name = "Sklivvz";
Run Code Online (Sandbox Code Playgroud)
假设 p 必须是一个 Interface(根据前面的推论),那么 Name 必须是一个字段成员,并且从赋值看来它的类型是 String。
就此,声明如下:
Assert.AreEqual("Sklivvz", p.Name);
Run Code Online (Sandbox Code Playgroud)
名称和类型解析后不会出现进一步的问题。
IFoo 和 Foo 实体的内容有点取决于您;您不必使用 get 和 set 但这是个人喜好。
当同一个语句中有多个实体时,这不会很好地工作:
x = p.a + p.b ;
Run Code Online (Sandbox Code Playgroud)
我们知道 a 和 b 可能是字段,但如果它们确实是数字,或者它们是字符串(这对于 Java 中的字符串是合法的,不知道 C# 是否如此),则您无法猜测它们是什么数字类型。对于C++,你甚至不知道“+”是什么意思;它可能是 Bar 类的运算符。因此,您要做的就是收集约束,例如“a 是某个不定的数字或字符串”等,并且当该工具收集证据时,它会缩小可能的约束集。(这就像那些文字问题:“乔有七个儿子。杰夫比山姆高。哈利不能躲在山姆后面……谁是杰夫的双胞胎?”你必须收集证据并消除不可能的情况)。你还必须担心最终会出现矛盾的情况。
你可以排除 p.a+pb 的情况,但是这样你就不能不受惩罚地编写你的单元测试。如果您想不受惩罚,可以使用标准约束求解器。(什么概念)。
好的,我们有了想法,现在可以用实际的方式来实现吗?
第一部分需要一个解析器和一个可弯曲的名称和类型解析器。您需要一个约束求解器或至少一个“定义值流向未定义值”操作(简单约束求解器)。
我们的DMS 软件重组工具包及其Java 前端可能可以做到这一点。DMS 是一个工具构建者的工具,适合那些想要构建以任意方式处理计算机语言的工具的人们。(想想“用程序片段而不是数字进行计算”)。
DMS 提供通用解析机制,并且可以为给定的任何前端(例如,Java,并且有一个 C# 前端)构建树。我选择 Java 的原因是我们的 Java 前端拥有所有名称和类型解析机制,并且它以源代码形式提供,因此可以进行修改。如果您坚持使用简单的约束求解器,您可能可以弯曲 Java 名称解析器来找出类型。DMS 可以让你组装与代码片段相对应的树,并将它们合并成更大的树;当您的工具收集符号表的事实时,它可以构建原始树。
在某个地方,你必须决定你已经完成了。该工具必须查看多少个单元测试才能了解整个界面?(我猜它会吃掉你提供的所有东西?)。完成后,它会组装各个成员的片段并为接口构建 AST;DMS 可以使用其 PrettyPrinter 将 AST 转换回源代码,如您所示。
我在这里建议使用 Java,因为我们的 Java 前端具有名称和类型解析。我们的 C# 前端没有。这“仅仅”是一个野心问题;必须有人编写一个,但这是一项相当大的工作(至少对于 Java 来说是这样,我无法想象 C# 真的有什么不同)。
但原则上,使用 DMS 的想法效果很好。
您可以使用其他一些基础设施来完成此操作,这些基础设施使您可以访问解析器以及可弯曲的名称和类型解析器。对于 C# 来说,这可能不太容易实现;我怀疑微软可能会给你一个解析器,并访问名称和类型解析,但没有任何方法可以改变它。也许 Mono 就是答案?
您仍然需要一个 is 来生成代码片段并组装它们。您可以尝试通过字符串黑客来做到这一点;我将程序位粘合在一起的(长期)经验是,如果你用字符串来做,你最终会把它弄得一团糟。您确实想要表示已知类型的代码片段的片段,这些片段只能以语法允许的方式组合;DMS 做到了这一点,因此不会造成任何混乱。