microsoft moles, dynamically instrument
Asked Answered
U

1

6

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.

Umbelliferous answered 7/6, 2011 at 22:12 Comment(9)
Assuming you have created MyMolesAssemply.dll, are you including the assembly and type attributes in the code? I suspect the Moles system may be cataloging instrumented types and assemblies, possibly by way of the ___.moles files.Critta
I need more than 600 chars to reply to your comment, so I'm adding more detail to this original question.Umbelliferous
So, using the new naming scheme I just added to the question above: I'll assume "MyMolesAssembly" refers to MyMoles. I think your suspicision is not correct for I can remove the MyMoles ref from Test, and ref the Moles.dll and it works. Note that Test does NOT have a __.moles file, nor ever did. The Moles.dll was created from another Samples project (which had the __.moles file).Umbelliferous
Have you added MyMoles.dll as a test project reference?Critta
Yes indeed. In fact I found something very curious. If Test refs both MyMoles and Moles, MyMoles still fails. If I touch the Moles' M___ class, MyMoles works. My 'touch' is a simple var type = typeof(M___) where the M___ type is in the Moles.dll. This leads to be believe there is some manifest rule that is executing. I need to find this manifest.Umbelliferous
I am unable to locate the manifest, at this time. However, I created a project and generated a mole of Sealed.dll in the test project, and created a mole test. Then, I deleted the Sealed.moles file, removed references to Dealed.Moles, and changed the name of the .dll and .xml files in the MolesAssemblies directory. Everything worked fine. I even moved the files to the "packages" solution directory. Perhaps a compiler switch is set that somehow signs the binary as a Mole assembly. My reflector is broken, but I'll try extracting code as you did.Critta
Your reflecttor is broken? Do you use lutz reflector that redgate has robbed from us!? Try out ILSpy, it's awesome (although how funny it would be if the problem is that I am missing a piece that ILSpy is failing to identify). Back to your comment, I'm a little unclear on the step "removed refs to Dealed.Moles" ? You meant Sealed I assume, but are you saying you only tested running the injection, but didn't verify the injection? If so, that is sufficient to what I'm saying for the step to inject is where the exception is thrown.Umbelliferous
I have reproduced your issue. Very interesting. My response is too long for a comment. Please see answer, below. (Is it wrong to make a decompiler decompile itself?)Critta
I'm having the same issue. Since Moles has issues reading the App.config files, I need to use Moles itself to reroute calls to ConfigurationManager to manually load the app.config file. I wanted to abstract this functionality, so I created a derived TestClassExtensionAttribute that would handle all the logic for instrumenting the App.Config there. That way, I can just put "MolesTestClass" on my test class, and the App.config would just work. Unfortunately, I need to add the [assembly: MoledAssembly(typeof(ConfigurationManager))] at the top of my Unit Test class itself. VERY leaky abstraction..Adulterine
C
4

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);
        }
    }
}
Critta answered 9/6, 2011 at 16:37 Comment(5)
Huge thanks for your efforts and interest to resolve this issue with me. Unfortunately, what you have arrived at is at the root of the problem, not the solution. Best effort to clarify...if you (1) take this test project you've created and ADD a ref to your ClassLibrary2.Moles, (2) remove your assembly attribute, & (3) remove ref to MoleClassLibrary, your code works. WITHOUT the assembly reference. HOW is this happening? How can adding the ClassLibrary2.Moles assembly remove the requirement to specify the assembly attribute within your test project? (+1 for enormously awesome effort)Umbelliferous
You are correct. My goal was to prove the Moles Tools for Visual Studio automatically handles identifying auto-generated mole assemblies to the compiler. Because the moles framework is unaware of the manual assembly, the attribute is required to alert the compiler to its presence. Such is the purpose of Assembly attributes. As you suggested, I right-clicked the ClassLibrary2 reference; selected ADD MOLES ASSEMBLY; build (adds reference automatically); Removed attribute; removed ref; (deleted MoleClassLibrary.moles); (added "using ClassLibrary1.Moles"). Class1Test works.Critta
I edited this answer, to directly answer your question. Very good question. It would be nice to see the Moles Tools for VS automatically pick up manually moled assemblies.Critta
Well to be honest I am unsatisfied with the situation. I tested running moles on a machine without moles 'installed'. There are necessary reg keys and gac stuff for the hostype for moles to work - and I'm sure other things. Needless to say, isolation sans moles-autogen appears to be out of reach.Umbelliferous
Well, if you have the Moles Framework installed and include the assembly attribute, it should work. I understand how this may be unsatisfactory!Critta

© 2022 - 2024 — McMap. All rights reserved.