Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

microsoft moles, dynamically instrument

Tags:

c#

.net

moles

Moles can be used in two ways:

Manually

  1. Including [assembly: MoledType(typeof(_type_to_instrument))]
  2. Specify [HostType("Moles")]
  3. Call Microsoft.Moles.Framework.Moles.MoleRuntime.SetMole(Delegate _stub, object _receiver, MethodInfo method);

Dynamically

  1. Add a {project name}.moles file: specifying assembly to mole. e.g. <Moles xmlns="http://schemas.microsoft.com/moles/2010/"> <Assembly Name="Samples.Moles"/> </Moles>
  2. Build and include reference to MolesAssemblies/{project_name}.Moles.dll
  3. Use M{class_name} auto-generated mole classes.

What I noticed is that using the dynamic assembly doesn't require the test project to declare 'moled assemblies' attributes. This reduces the overhead, and a developer need only decorate each test method with the host type for moles; but further testing doesn't need to keep track of what types to instrument.

Looking at the auto-generated code (using a disassembler) in the molesassemblies, it is easy to find that the needed instrumentation attributes. However, trying to write my own 'mole assembly', essentially replacing the autogenerated one, doesn't work and the runtime complains that my type needs to be instrumented. I am curious what I'm missing.

I noticed that the autogenerated moles code declared the necessary MoledAssembly attributes. But in my tests, the test project seems to have to declare this property; it can't be declared by a referenced assembly to the project. However, in the case with the autogenerated assembly, it APPEARS the attribute can be declared "outside". This is my assumption based what I can see with disassembling the autogenerated moles dll; I cannot find any other difference. However, as I'm trying to explain, copying all the code (and attributes) from the disassembled autogenerated moles dll and building my own referenced assembly fails at runtime saying I have not marked the needed assembly-in-test to be instrumented (i.e. marked with the MoledAssembly) - tho it has, simply in my referenced asssembly.

-- update

At this point (likely due to my misunderstanding of what my code is missing) I feel we need to be very specific about what assembly has what. Let's say we have 4 dlls:

  1. Test.dll: The mstest project. Does not declared MoledAssembly.
  2. Moles.dll: The autogenerated dll created when using a *.moles file in your project. References the 4th dll, (see #4) Sealed. Declares [assembly: MoledAssembly("Sealed")]. Note that I'm trying to accomplish manual mole injection without this dll - it's only a conceptual reference or to be used in our discussion or troubleshooting.
  3. MyMoles.dll: My from-source compiled version of the autogenerated Moles.dll.
  4. Sealed.dll: Contains the code-under-test.

In answers/comments/questions - let's refer to each part according to this list.

like image 293
payo Avatar asked Jun 07 '11 22:06

payo


1 Answers

The assembly attribute is required, when using moled assemblies that are not automatically generated. The Moles Tools for Visual Studio automatically alert the compiler to the presence of generated assemblies, out of necessity.

When adding a Moles assembly via Visual Studio, the mole assembly is not generated until the project is built. Furthermore, it is impossible to include an assembly attribute for an assembly that does not already exist. Doing so will cause the compiler to fail. Therefore, it is necessary for Moles to also dynamically add commands to the compiler command line, to generate the moled assemblies, and then properly reference them from the project.

When using a manually-generated, moled assembly, it is necessary to include an assembly attribute, because the Moles tools is unaware of its presence by virtue of the assembly not being auto-generated. The programmer has to do the job for Moles.

If you want to go so far, you may employ code generation, before the compiler engages. PERL can easily inject the necessary assembly attribute, where needed. When the compiler receives the code, it will already have the attribute(s) injected.

An Experiment to Back my Answer:

I was able to reproduce your issue. I was also able to resolve the problem, by adding an assembly attribute, below the using statement block. I took the following steps, to build my sample application:

  1. Created a .NET 4.0 C# Class Library project, named ClassLibrary2.
  2. Created the following method, in Class1:

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

  3. Created a test project (TestProject1), by right-clicking the TestString method declaraction, and then selecting Create Unit Tests... (Lazy, I know.)

  4. Deleted the extra crap out of Class1Test.cs, leaving TestStringTest().
  5. Added a moles assembly for mscorlib. (Another lazy shortcut, and an unnecessary step, in retrospect. I note it here, because it is something I did.)
  6. Added a moles assembly for ClassLibrary2.
  7. Compiled solution using the default Any CPU profile.
  8. Used Redgate (sorry, @payo) to decompile ClassLibrary2.Moles
  9. Added a new Class Library project, named MoleClassLibrary.
  10. Copied the decompiled MClass1 and SClass1 code into MoleClassLibrary.
  11. Removed the Class1.moles file and assemblies from TestProject1.
  12. Removed the (unnecessary) mscorlib.moles file and assemblies from TestProject1.
  13. Added a MoleClassLibrary reference to TestProject1.
  14. Updated the using statement in Class1Test.cs.
  15. Build solution.
  16. Executed TestStringTest(), using the Visual Studio 2010 Test View window.
  17. The test fails, producing details:

Test method TestProject1.Class1Test.TestStringTest threw exception: Microsoft.Moles.Framework.Moles.MoleNotInstrumentedException: The System.String ClassLibrary1.Class1.TestString() was not instrumented To resolve this issue, add the following attribute in the test project:

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

I added the recommended assembly attribute to the file. After doing so, the test method ran successfully. I suspect the compiler is automatically referencing generated moled assemblies, removing the need for the assembly attribute. I tried copying the MoleClassLibrary binaries to the MolesAssemblies directory and creating a MoleClassLibrary.moles file, to test this theory. The test passed only when I included the assembly attribute. This result is inconclusive to my hypothesis.

Here's the code for 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);
        }
    }
}
like image 137
Mike Christian Avatar answered Sep 18 '22 11:09

Mike Christian