Microsoft Moles动态仪器化

6

摩尔斯代码可以有两种使用方式:

手动

  1. 包含 [assembly: MoledType(typeof(_type_to_instrument))]。
  2. 指定 [HostType("Moles")]。
  3. 调用 Microsoft.Moles.Framework.Moles.MoleRuntime.SetMole(Delegate _stub, object _receiver, MethodInfo method)。

动态

  1. 添加一个 {project name}.moles 文件:指定需要模拟的程序集。例如: <Moles xmlns="http://schemas.microsoft.com/moles/2010/"> <Assembly Name="Samples.Moles"/> </Moles>
  2. 构建并包含对 MolesAssemblies/{project_name}.Moles.dll 的引用。
  3. 使用自动生成的 M{class_name} 模拟类。

我注意到使用动态程序集不需要测试项目声明“摩尔斯程序集”属性。这减少了开销,开发人员只需为每个测试方法添加摩尔斯主机类型装饰器;但进一步的测试不需要跟踪要检测的类型。

查看 molesassemblies 中的自动生成代码(使用反汇编器)很容易找到所需的仪器化属性。然而,尝试编写自己的“摩尔斯程序集”,即替换自动生成的程序集,却无法工作,并且运行时会抱怨我的类型需要被仪器化。我很好奇我错过了什么。

我注意到自动生成的摩尔斯代码声明了必要的 MoledAssembly 属性。但在我的测试中,测试项目似乎必须声明此属性;它不能由引用项目来声明。然而,在自动生成程序集的情况下,属性似乎可以被“外部”声明。这是我根据反汇编自动生成的 moles dll 所看到的推断;我找不到其他区别。然而,正如我试图解释的那样,从反汇编自动生成的 moles dll 复制所有代码(和属性),并构建我的自己的引用程序集,在运行时失败,说我没有标记所需的测试程序集以进行仪器化(即标记为 MoledAssembly)- 尽管它已经在我的引用程序集中。

-- 更新

此时(可能是由于我对我的代码缺失的理解),我感觉我们需要非常具体地说明哪个程序集有什么。假设我们有 4 个 DLL:

  1. Test.dll: 这是mstest项目。没有声明MoledAssembly
  2. Moles.dll: 当在您的项目中使用*.moles文件时自动生成的dll。引用第四个dll(见#4)Sealed。声明[assembly: MoledAssembly("Sealed")]。请注意,我正在尝试在没有此dll的情况下进行手动注入 - 它仅是一个概念性参考或用于我们的讨论或故障排除。
  3. MyMoles.dll: 我从源代码编译的自动生成的Moles.dll版本。
  4. Sealed.dll: 包含要测试的代码。

在答案/评论/问题中,让我们按照此列表中的每个部分进行参考。


假设您已经创建了MyMolesAssemply.dll,您是否在代码中包含了程序集和类型属性?我怀疑Moles系统可能正在通过___.moles文件目录化检测到的类型和程序集。 - Mike Christian
我需要超过600个字符来回复您的评论,因此我正在为这个原始问题添加更多细节。 - payo
因此,使用我刚刚添加到上面问题的新命名方案:我将假设“MyMolesAssembly”指的是MyMoles。我认为你的怀疑是不正确的,因为我可以从Test中删除MyMoles引用,并引用Moles.dll,它可行。请注意,Test没有___.moles文件,也从未有过。 Moles.dll是从另一个示例项目(其中包含__.moles文件)创建的。 - payo
确实是。事实上,我发现了一些非常奇怪的东西。如果Test引用了MyMoles Moles,那么MyMoles仍会失败。如果我触碰Moles的M___类,则MyMoles可以正常工作。我的“触碰”是一个简单的var type = typeof(M___),其中M___类型在Moles.dll中。这让我相信有一些执行清单规则。我需要找到这个清单。 - payo
1
我目前无法找到清单。但是,我创建了一个项目并在测试项目中生成了Sealed.dll的模拟程序,并创建了一个模拟测试。然后,我删除了Sealed.moles文件,删除了对Dealed.Moles的引用,并更改了MolesAssemblies目录中.dll和.xml文件的名称。一切都正常工作。我甚至将文件移动到“packages”解决方案目录中。也许编译器开关被设置为以某种方式将二进制文件签名为模拟程序集。我的反射器坏了,但我会像你一样尝试提取代码。 - Mike Christian
显示剩余4条评论
1个回答

4
使用moled程序集时,如果不是自动生成的,则需要使用装配属性。由于必要性,Visual Studio的Moles工具会自动向编译器发出生成程序集的信号。
通过Visual Studio添加Moles程序集后,该程序集将在构建项目时生成。此外,不可能为不存在的程序集包括装配属性,这样做会导致编译器失败。因此,Moles还需要动态向编译器命令行添加命令,以生成moled程序集,并从项目中正确引用它们。
当使用手动生成的moled程序集时,需要包括装配属性,因为Moles工具不知道其存在,因为该程序集不是自动生成的。程序员需要为Moles完成这项工作。
如果想进一步操作,可以在编译器开始之前使用代码生成。PERL可以轻松注入所需的装配属性。当编译器接收到代码时,它已经注入了属性。 一个支持我的答案的实验: 我能够重现您的问题。我也能够通过在using语句块下面添加装配属性来解决问题。我采取了以下步骤来构建我的示例应用程序:
  1. 创建了一个.NET 4.0 C#类库项目,命名为ClassLibrary2。
  2. 在Class1中创建了以下方法:

    public string TestString() { return "Original value."; }

  3. 通过右键单击TestString方法声明,然后选择创建单元测试...创建了一个测试项目(TestProject1)。(懒惰,我知道。)

  4. 删除了Class1Test.cs中多余的内容,只留下TestStringTest()。
  5. 添加了mscorlib的moles程序集。(另一种懒惰的快捷方式,在回顾时是不必要的步骤。我在这里记录它,因为这是我做过的事情。)
  6. 添加了ClassLibrary2的moles程序集。
  7. 使用默认的Any CPU配置编译解决方案。
  8. 使用Redgate (抱歉,@payo)来反编译ClassLibrary2.Moles。
  9. 添加了一个新的类库项目,命名为MoleClassLibrary。
  10. 将反编译后的MClass1和SClass1代码复制到MoleClassLibrary。
  11. 从TestProject1中删除了Class1.moles文件和程序集。
  12. 从TestProject1中删除了(不必要的)mscorlib.moles文件和程序集。
  13. 向TestProject1添加了一个MoleClassLibrary引用。
  14. 在Class1Test.cs中更新了using语句。
  15. 构建解决方案。
  16. 使用Visual Studio 2010 Test View窗口执行了TestStringTest()。
  17. 测试失败,生成详细信息:

测试方法TestProject1.Class1Test.TestStringTest引发异常:Microsoft.Moles.Framework.Moles.MoleNotInstrumentedException: System.String ClassLibrary1.Class1.TestString()没有被检测要解决此问题,请在测试项目中添加以下属性:

使用Microsoft.Moles.Framework;[assembly: MoledAssembly(typeof(ClassLibrary1.Class1))]

我在文件中添加了推荐的程序集属性。这样做后,测试方法成功运行。我怀疑编译器自动引用生成的moled程序集,因此不需要程序集属性。我尝试将MoleClassLibrary二进制文件复制到MolesAssemblies目录并创建一个MoleClassLibrary.moles文件以测试这个理论。只有当我包括程序集属性时,测试才通过。这个结果对我的假设来说是无法确定的。

下面是Class1Test.cs的代码:

using ClassLibrary1;
using Microsoft.Moles.Framework;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using MoleClassLibrary;
[assembly: MoledAssembly(typeof(ClassLibrary1.Class1))]

namespace TestProject1
{
    [TestClass()]
    public class Class1Test
    {
        [TestMethod()]
        [HostType("Moles")]
        public void TestStringTest()
        {
            var target = new Class1();
            var expected = "Mole value.";
            string actual;
            MClass1.AllInstances.TestString = value => expected;
            actual = target.TestString();
            Assert.AreEqual(expected, actual);
        }
    }
}

非常感谢您的努力和兴趣,与我一起解决这个问题。不幸的是,您所得到的根源是问题,而不是解决方案。尽最大努力澄清......如果您(1)取此测试项目并添加对ClassLibrary2.Moles的引用,(2)删除程序集属性,以及(3)删除对MoleClassLibrary的引用,则您的代码将正常工作。没有程序集引用,会发生什么?如何添加ClassLibrary2.Moles程序集可以消除在测试项目中指定程序集属性的要求?(+1,非常棒的努力)。 - payo
你说得对。我的目标是证明Visual Studio的Moles工具可以自动处理识别自动生成的mole程序集给编译器。因为mole框架不知道手动程序集,所以需要使用属性来提醒编译器其存在。这就是程序集属性的目的。正如你建议的那样,我右键单击了ClassLibrary2引用;选择ADD MOLES ASSEMBLY;构建(自动添加引用);删除属性;删除引用;(删除MoleClassLibrary.moles);(添加“using ClassLibrary1.Moles”)。Class1Test有效。 - Mike Christian
我编辑了这个答案,直接回答你的问题。非常好的问题。希望能看到 Moles 工具自动捕获手动捕获的程序集。 - Mike Christian
说实话,我对这种情况感到不满意。我在一台没有安装moles的机器上进行了运行测试。为了使moles正常工作,主机类型需要有必要的注册表键和gac文件。当然,没有moles-autogen的隔离似乎是无法实现的。 - payo
如果您已安装了Moles框架并包含程序集属性,它应该可以工作。我理解这可能是不令人满意的! - Mike Christian

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接