May 2006 - Posts

Upgrading from NUnit 2.2.0 to 2.2.8 minor issues

I just tried out the 2.2.8 release of NUnit today and came across an odd situation.

My .nunit project contained approx 900 unit tests when loaded under the 2.2.0 gui and yet when I load them in the 2.2.8 gui I have nearly 1200 and I also received an error claiming that tests were duplicated and would not be displayed correctly in the gui!!

The culprit == attributes.

When I create an interface definition I like to create a corresponding interface test definition which I can implement on the test class for any production class implementing the interface:

public interface IInstrumentMeta

{

#region Events

#endregion

#region Methods

#endregion

#region Properties

string Currency{get;set;}

string Index{get;set;}

string Product{get;set;}

string Description{get;set;}

string UniqueId{get;set;}

#endregion

}

Test interface:

public interface IInstrumentMetaTest

{

#region Test Methods

[Test]

void CurrencyTest();

[Test]

void IndexTest();

[Test]

void ProductTest();

[Test]

void DescriptionTest();

[Test]

void UniqueIdTest();

#endregion

}

You will notice that the methods are marked up as [Test] but that the interface is not marked up as [TestFixture]. If you try, the compiler gives you:

Attribute 'TestFixture' is not valid on this declaration type. It is valid on 'class' declarations only.

This makes sense - we cannot instantiate an interace and run the tests after all. Any test class implementing the interface should be marked up as [TestFixture] so we're fine.

What caught me out is that you can apply the attribute to an abstract class....

If I create abstract classes I also like to create abstract base classes which implement test methods for any virtualised methods etc and define abstract test methods for any declared abstract methods etc. In this way, I can guarantee that any virtual base methods are tested within the context of the inherited concrete class during testing. I was marking up the abstract base test classes with [TestFixture] attribute which did not cause any issue with 2.2.0.

However, 2.2.8 attempts to load and run the abstract classes because they are marked up as fixtures.... the tests are ignored automatically by the gui because the test classes have no valid constructor but it does explain why I suddenly seemed to have gained a few hundred unit tests.

Solution == remove the [TestFixture] attribute from all abstract test classes - you should leave the [Test] attribute on the methods though - exactly as with an interface test class.

Mocking HttpContext using TypeMock.Net

Here's a basic guide on setting up mocking of HttpContext using TypeMock.Net - this is *really* useful for writing test fixtures for your .aspx code-behind files and front page controller pattern commands which reference the current Context for example since there isn't an HttpContext etc when running the tests under NUnit. I have used this technique extensively in my current ASP.Net project but have yet to try it within an Mcms or Sps project although it should apply equally as well.

Unfortunately, TypeMock.Net is not free but for the mocking power it allows I believe it's well worth the price - find their pricing and feature comparisons here.

The principle of this revolves around TypeMock's ability to mock methods and properties etc within concrete classes including selectively mocking methods in the class you're actually testing! I also make use of some reflection code to invoke internal constructors for mocking internal concrete classes and protected methods on our production class.

First a simple example requiring no dependency injection or selective mocking:

Production class:

/// <summary>

/// Command would usually implement some kind of ICommad interface but for simplicity in this

/// example this has been omitted

/// </summary>

public class TestCommand

{

public TestCommand()

{

}

/// <summary>

/// Execute required actions

/// </summary>

/// <param name="httpContext">HttpContext.Current</param>

protected void OnExecute(HttpContext httpContext)

{

// get an HttpContext property

DateTime currentDateTime = httpContext.Timestamp;

// get an HttpContext.HttpSessionState property

string sessionId = httpContext.Session.SessionID;

// get an HttpContext.Response property

string responseStatus = httpContext.Response.Status;

}

}

 Test Class:

[TestFixture]

public class TestCommandTest

{

#region Protected Fields

protected TestCommand testCommand;

protected Mock mockHttpContext;

protected HttpContext httpContext;

protected Mock mockHttpSessionState;

protected HttpSessionState httpSessionState;

protected Mock mockHttpResponse;

protected HttpResponse httpResponse;

#endregion

#region Test Methods

[SetUp]

public void SetUp()

{

// initialize the mockmanager

MockManager.Init();

// initialize a mockHttpContext and new an HttpContext for the mock

this.mockHttpContext = MockManager.Mock(typeof(HttpContext), Constructor.Mocked);

this.httpContext = new HttpContext(null);

// initialize a mockHttpSessionState and new (using reflection because .ctor is internal) an HttpSessionState

// I used NetReflector to find the .ctor definition

this.mockHttpSessionState = MockManager.Mock(typeof(HttpSessionState), Constructor.Mocked);

ConstructorInfo[] constructorInfos = typeof(HttpSessionState).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance);

this.httpSessionState = (HttpSessionState) constructorInfos[0].Invoke(new object[]{null, null, null, null, null, null, null, null});

// initialize a mockHttpResponse and new an HttpResponse for the mock

this.mockHttpResponse = MockManager.Mock(typeof(HttpResponse), Constructor.Mocked);

this.httpResponse = new HttpResponse(null);

// instantiate our test class

this.testCommand = new TestCommand();

}

[TearDown]

public void TearDown()

{

// verify our mocks

MockManager.Verify();

}

[Test]

public void OnExecuteTest()

{

//configure mockHttpContext to return the Timestamp property

DateTime expectedDateTime = new DateTime(2000, 1, 1);

this.mockHttpContext.ExpectGet("Timestamp", expectedDateTime, 1);

// configure the mockHttpContext to return the mockHttpSessionState

this.mockHttpContext.ExpectGet("Session", this.httpSessionState, 1);

// configure the mockHttpSessionState to return the SessionID

string expectedSessionId = "uyiuytieur';';43';5";

this.mockHttpSessionState.ExpectGet("SessionID", expectedSessionId, 1);

// configure the mockHttpContext to return the mockHttpResponse

this.mockHttpContext.ExpectGet("Response", this.httpResponse, 1);

// configure the mockHttpResponse to return the Status

string expectedStatus = "*&^*&S^*&^D*&^D*&^";

this.mockHttpResponse.ExpectGet("Status", expectedStatus, 1);

// construct the args for the wrapped reflection call

object[] args = new object[]{this.httpContext};

// invoke the method to test

this.InvokeMethod("OnExecute", args);

}

#endregion

#region Protected Members

protected object InvokeMethod(string methodName, object[] args)

{

MethodInfo methodInfo = this.testCommand.GetType().GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance);

return methodInfo.Invoke(this.testCommand, args);

}

#endregion

}

//NB//

  • notice that I had to use reflection to instantiate the HttpSessionState - the class and its .ctor are internal but reflection allows us to instantiate and operate it.
  • the return values from the mocked methods are mostly complete garbage but they are of the correct type - this clearly establishes whether the mocked method executed during debugging

OK - so we can construct and operate a mock HttpContext when we pass it as an argument.... what about an .aspx code-behind where the Context is already present within the page????

e.g.

Production class:

public class Foo : System.Web.UI.Page

{

private void Page_Load(object sender, System.EventArgs e)

{

// Put user code to initialize the page here

}

#region Web Form Designer generated code

override protected void OnInit(EventArgs e)

{

//

// CODEGEN: This call is required by the ASP.NET Web Form Designer.

//

InitializeComponent();

base.OnInit(e);

}

/// <summary>

/// Required method for Designer support - do not modify

/// the contents of this method with the code editor.

/// </summary>

private void InitializeComponent()

{

this.Load += new System.EventHandler(this.Page_Load);

}

#endregion

protected void ExampleMethod()

{

DateTime currentDateTime = this.Context.Timestamp;

string sessionId = this.Context.Session.SessionID;

string responseStatus = this.Context.Response.Status;

return;

}

}

Well - we use selective mocking to inject our own configured mocks into the Page via the Context property inherited from the Page class:

Test Fixture code:

[TestFixture]

public class fooTest

{

#region Protected Fields

protected Mock mockFoo;

protected Foo foo;

protected Mock mockHttpContext;

protected HttpContext httpContext;

protected Mock mockHttpSessionState;

protected HttpSessionState httpSessionState;

protected Mock mockHttpResponse;

protected HttpResponse httpResponse;

#endregion

#region Test Methods

[SetUp]

public void SetUp()

{

// initialize the mockmanager

MockManager.Init();

// initialize a mockHttpContext and new an HttpContext for the mock

this.mockHttpContext = MockManager.Mock(typeof(HttpContext), Constructor.Mocked);

this.httpContext = new HttpContext(null);

// initialize a mockHttpSessionState and new (using reflection because .ctor is internal) an HttpSessionState

// I used NetReflector to find the .ctor definition

this.mockHttpSessionState = MockManager.Mock(typeof(HttpSessionState), Constructor.Mocked);

ConstructorInfo[] constructorInfos = typeof(HttpSessionState).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance);

this.httpSessionState = (HttpSessionState) constructorInfos[0].Invoke(new object[]{null, null, null, null, null, null, null, null});

// initialize a mockHttpResponse and new an HttpResponse for the mock

this.mockHttpResponse = MockManager.Mock(typeof(HttpResponse), Constructor.Mocked);

this.httpResponse = new HttpResponse(null);

// initialize a mock of the production class

this.mockFoo = MockManager.Mock(typeof(Foo), Constructor.NotMocked);

// Strict = false configures TypeMock to allow unexpected calls and execute the original code

this.mockFoo.Strict = false;

// instantiate our production class

this.foo = new Foo();

}

[TearDown]

public void TearDown()

{

MockManager.Verify();

}

[Test]

public void ExampleMethodTest()

{

//configure a selective mock to force our page to return the mocked HttpContext

this.mockFoo.ExpectGet("Context", httpContext, 3);

//configure mockHttpContext to return the Timestamp property

DateTime expectedDateTime = new DateTime(2000, 1, 1);

this.mockHttpContext.ExpectGet("Timestamp", expectedDateTime, 1);

// configure the mockHttpContext to return the mockHttpSessionState

this.mockHttpContext.ExpectGet("Session", this.httpSessionState, 1);

// configure the mockHttpSessionState to return the SessionID

string expectedSessionId = "uyiuytieur';';43';5";

this.mockHttpSessionState.ExpectGet("SessionID", expectedSessionId, 1);

// configure the mockHttpContext to return the mockHttpResponse

this.mockHttpContext.ExpectGet("Response", this.httpResponse, 1);

// configure the mockHttpResponse to return the Status

string expectedStatus = "*&^*&S^*&^D*&^D*&^";

this.mockHttpResponse.ExpectGet("Status", expectedStatus, 1);

this.InvokeMethod("ExampleMethod", null);

}

#endregion

#region Protected Members

protected object InvokeMethod(string methodName, object[] args)

{

MethodInfo methodInfo = this.foo.GetType().GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Instance);

return methodInfo.Invoke(this.foo, args);

}

#endregion

}

By selectively mocking the Context property and returning our mocked up HttpContext (which in turn returns mocked HttpSessionState and HttpResponse objects) we can safely and quickly test our code-behind pages without the need for a real context.

I know many of you will be looking at the quantity and apparent complexity of the code required to create our test case and be thinking that the test code outweighs the production code by quite a margin and is too much work!!! I would agree - BUT ;0) - create a base PageTest class which declares the protected Mock fields and use a SetUp to initialize them etc. Then whenever you create a new c.aspx page you create a new test class and derive it from your BasePageTest and :fanfare: you already have the mocks initialized and ready-to-go - just set whatever expectations are required for the method you are testing and you are ready to rock!

Although I have tried it yet I don't see amy reason why Mcms and Sharepoint contexts could not be mocked during unit testing in exactly the same way.... just use Lutz Roeder's excellent NetReflector to find the constructors and away you go!

Creating a build responsive to changes in its configuration.

This is mostly covered by reading all the ccnet server config documentation but I thought I would summarise and add a little - I am using a nant based configuration where cruise control only checks for modifications and then launches nant to perform the rest of the build.

A great config I have found to be very easily maintainable is to have 3 files for your build which I will conveniently name (you should replace {project} with your project name:

{project}.ccnet.config - configures cruise to run your build and if changes are found launches the bootstrap nant file.

{project}.bootstrap.nant.config - performs the bootstrap actions of a build: delete source, checkout fresh source, manage log files to prevent filling disk, launch the build file ({project}.nant.config.

{project}.nant.config - compiles, runs unit tests, vil analysis, fxcop, ncover etc....

The ccnet config contains a project element which you configure as per your requirements - you reference the {project}.ccnet.config from the ccnet.config using an xml entity reference as follows:

<!--
 this is an example of how the ccnet.config *should* look on the build server to allow
 separate config files per project
-->

<!DOCTYPE cruisecontrol [
 <!ENTITY {project} SYSTEM "file:{project}.ccnet.config">
]>
<cruisecontrol>
 &{project};
</cruisecontrol>

The immediate benefit of this approach is that you don't have 1 massive ccnet.config file if you wanna run 4 builds - you have 1 small ccnet.config and 4 more easily manageable project config files. This file needs to be copied to the project ccnet server root.

An example {project}.ccnet.config:

<project>
 <name>{project}</name>
 <modificationDelaySeconds >10</modificationDelaySeconds>
 <publishExceptions>true</publishExceptions>
 
 <sourcecontrol type="cvs">
       <executable>cvs.exe</executable>
       <cvsroot>{your cvsroot}</cvsroot>
       <workingDirectory>{working directory}</workingDirectory>
       <branch></branch>
       <timeOut units="minutes">5</timeOut>
       <labelOnSuccess>true</labelOnSuccess>
    </sourcecontrol>
 
 <triggers>
  <intervalTrigger seconds="60" buildCondition="IfModificationExists" />
 </triggers>
 
 <state type="state" directory="{working directory}" />
 
 <tasks>
  <nant>
   <executable>nant.exe</executable>
   <buildFile>{project}.bootstrap.nant.config</buildFile>
   <buildArgs></buildArgs>
   <targetList>
    <target>bootstrap</target>
   </targetList>
   <buildTimeoutSeconds>600</buildTimeoutSeconds>
  </nant>
 </tasks>
 
 <publishers>
  <merge>
   <files>
    <file>{working directory}\*.NUnitResults.xml</file>
    <file>{working directory}\*.NCoverResults.xml</file>
    <file>{working directory}\*.VilResults.xml</file>
   </files>
  </merge>
  <xmllogger>
   <logDir>Logs</logDir>
  </xmllogger>
    </publishers>
</project>

You'll notice that the task is set to launch a nant script using script - I have also called the file .config rather than the accepted .build because Visual Studio 2003 gives my contextual colouring for .config file which it doesn't for .builds unless you fiddle with the settings.

An example bootstrap:

<project name="{project}.BootStrap" default="bootstrap">
 
 <target name="bootstrap" depends="stopiis, clean, checkout, startiis, build"/>

 <target name="stopiis" depends="" description="" >
  <exec
   program="iisreset"
   commandline="/stop"
  />
 </target>
 
 <target name="startiis" depends="" description="" >
  <exec
   program="iisreset"
   commandline="/start"
  />
 </target>

 <target name="clean" depends="" description="delete solution from working directory">
  <attrib normal="true">
   <fileset defaultexcludes="false">
    <include name="{project.root}/**" />
   </fileset>
  </attrib>
  <delete dir="{project.root}" />
 </target>
 
 <target name="checkout" depends="" description="checkout module from source">
  <exec
   workingdir="${cvs.destination}"
   program="${cvs.exe}"
   commandline="-Q checkout {cvs.branchlabel} {cvs.module}"
   output="{cvs.destination}\cvs.log"
  />
 </target>
 
 <target name="build" depends="">
  <nant
   buildfile="${project.root}\${nant.config}"
   target="${nant.target}"
  />
 </target>
</project>

I have a task to stop and restart IIS in this config because it's pulled from a project which builds a web application - if IIS is running it will maintain a file lock on the assemblies and prevent you from deleting the original source.

Also notice the nant task target points to the location where the final {project}.nant.config will be checked out to from source - this is how you can have your build respond to changes in its configuration simply by committing to source!!!! - the steps of building the solution and then running other tasks etc are all handled in the final {project}.nant.config file....

e.g.

<project name="{project}" default="integrate" >
 
 
<target name="integrate" depends="compile,nunit.ncover.report" />
 
 <target name="compile" depends="" description="compile solution">
  <exec
   failonerror="false"
   resultproperty="vsnet.exitcode"
   basedir="{vsnet.root}"
   program="vsnet.exe"
   commandline="${project.root}\${project.name}.sln /rebuild ${vsnet.config} /out ${project.root}\${project.name}.Build.txt"
  />
  <!--
   If the compile failed - email the vs log then fail the build
  -->
  <ifnot test="${property::get-value('vsnet.exitcode') == '0'}">
   <ifnot test="${property::exists('CCNetLabel')}">
    <fail message="Build failed :: CCNetLabel property not set!" />
   </ifnot>
   <mail
    failonerror="false"
    from="{from email address}
"
    tolist="{maillist}"
    mailhost="{mailhost}"
    message="Visual Studio .Net 2003 Build Output Log File Attached::"
    subject="Do Not Reply To This Message:: Failed Build (${CCNetLabel}): ${project.name}(${vsnet.config})"
   >
    <attachments>
     <include name="${project.root}\${project.name}.Build.txt" />
    </attachments>
   </mail>
   <fail message="External Program Failed: ${vsnet.root}\${vsnet.ide} (return code was ${vsnet.exitcode})" />
  </ifnot>
 </target>
 
 <target name="nunit.ncover.report" depends="" description="generate code coverage and unit test reports">
  
  <property name="project" value="{nunit project name}" />
  <property name="assemblies" value="{semi-colon delimited list of assemblies to monitor}" />
  <exec
   workingdir="{project.root}\lib"
   program="{ncover.exe}"
   commandline="/o &quot;{project.root}\${project}.Test.NCoverResults.xml&quot; /c &quot;${nunit.exe}&quot; &quot;{project}.nunit /nologo /xml={project.root}\{project}.Test.NUnitResults.xml&quot; /a &quot;${assemblies}&quot;"
  />
  
 </target>

  
</project>

I hope that you find this suggestion useful for creating a more maintainable and responsive build!

Avoid nasty disaster recovery for your ccnet instances.

It may seem obvious but I cannot tell you the number of times people have come a cropper because due to some maintenance task error their ccnet instance directory has been nuked and they have lost all their config files.

Check them into source!

I usually keep the little blighters in the root of my solution with my .sln file - when I make changes I commit them to source and remember to xcopy the new version to the ccnet server directory (a .bat file which you just double-click can be really useful here).

If your instance directory gets nuked - create the basic structure and just xcopy your configs, get a version from source for the mod check to work and you are back up and running in minutes.