# Monday, 18 December 2006

I spent about a day and a half this week trying to get a CC.NET server working with a complex combination of MSBuild (running the build script) TypeMock (the solution we're using for "mock objects") and NCover (for code coverage analysis) that proved tricky to get right.

In a typical build script, you'd create a task to run your unit tests that depends on your compile task.  And you might want to run a "clean" task first so you know you're starting from ground zero. 

Bringing a profiler into the mix changes the equation a bit.  Or in this case, two profilers that have to play nice together.  A "profiler" in this case means something that registers itself as a .NET profiler, meaning that it uses the underlying COM API in the CLR to get under the covers of the runtime.  This was originally envisioned as enabling profilers that track things like performance or memory usage that have to run outside the context of the CLR.  NCover uses this capability to do code coverage analysis, so that we can get reports of which lines of code in our platform are not being touched by our unit tests.

TypeMock is also a profiler, which causes some interesting interaction.  TypeMock uses the profiler API to insert itself between calling code and the ultimate target of that call in order to "mock" or pretend to be the target.  We use this to reduce the dependencies that our unit test code requires.  Rather than installing SQL Server on our build box, we can use TypeMock to "mock" the calls to the database, and have them return the results we expect without actually calling SQL. 

So...the problem all this interaction led to re: my build was that in order to make everything work, the profiler(s) have to be active before any of the assemblies you want to test/profile are loaded.  I had my UnitTest target set up like this:

 

<Target Name="UnitTestCoverage" DependsOnTargets="Compile;RemoveTestResults">

 

    <CreateItem Include="$(BuildDir)\\**\*.MBUnit.dll" >

      <Output

              TaskParameter="Include"

              ItemName="TargetFiles"/>

    </CreateItem>

 

    <TypeMockStart Link="NCover" />

 

    <Exec Command="&quot;$(NCoverConsole)&quot; $(MBUnitConsole) @(TargetFiles->'%(FullPath)', ' ') /rt:xml /rf:$(TestResultsDir) /fc:unit //l $(TestResultsDir)\NCover.log //x $(TestResultsDir)\NCoverage.xml //a $(AssembliesToProfile)" ContinueOnError="true"/>

 

    <TypeMockStop/>

    ...

</Target>

with the dependencies set to that the compile and "RemoveTestResults" targets would be evaluated first.  Unfortunately, this caused the whole build to hang for upwards of an hour when this target started, but after the compile and "remove" targets had run.  I'm theorizing (but haven't confirmed) that is is because the compiler loads the assemblies in question during the build process, and they don't get unloaded by the time we get to starting TypeMock.  That apparently means a whole bunch of overhead to attach the profilers (or something).  What finally ended up working was moving the compile target inside the bounds of the TypeMock activation, using CallTarget instead of the DependsOnTargets attribute.

 

    <TypeMockStart Link="NCover" />

    <CallTarget Targets="CleanCompile;RemoveTestResults"/>

 

    <Exec Command="&quot;$(NCoverConsole)&quot; $(MBUnitConsole) @(TargetFiles->'%(FullPath)', ' ') /rt:xml /rf:$(TestResultsDir) /fc:unit //l $(TestResultsDir)\NCover.log //x $(TestResultsDir)\NCoverage.xml //a $(AssembliesToProfile)" ContinueOnError="true"/>

 

    <TypeMockStop/>

This works just fine, and doesn't cause the 1 hour delay, which makes things much easier. :-)