Right now I am working on a CQRS project, and I am using WCF as my web service technology. In this project I decided to challenge myself and make a full TDD project. The first big problem I found was to simulate a real web server, because I didn’t want to hit a production or QA environment to run my tests.
Second I didn’t want to write any integration test at all, so I had to figure out how to create an in-Process test to run my WCF methods.
Why in-process test? When you write a unit test, you should test a single functionality, and in order to make the test atomic, it should run in-process. It should not involve third party tiers like a database or IIS, otherwise the test won’t be in-process anymore, so it will became an Integration test.
The first idea I came out was to fake the behaviour of IIS, and in order to accomplish this I could easily work with a mock framework like Typemock and mock out the entire IIS infrastructure.
Is this what I really want? Not at all. I don’t want and I don’t need to fake the behaviour of IIS, I need an instance of it because my WCF services are using custom factories and custom behaviours and I need to be sure they are acting in the right way.
So I have created two different type of tests. The first one is pure Unit Test, where I verify the command behaviours:
|
1 |
<span id="lnum1" style="color: #606060"> 1:</span> [TestMethod] |
|
1 |
<span id="lnum2" style="color: #606060"> 2:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span> AddUsersCommandHandler_ValidCommand_WillPersist() |
|
1 |
<span id="lnum3" style="color: #606060"> 3:</span> { |
|
1 |
<span id="lnum4" style="color: #606060"> 4:</span> <span style="color: #008000">// arrange</span> |
|
1 |
<span id="lnum5" style="color: #606060"> 5:</span> AddUsersCommand expectedCommand = <span style="color: #0000ff">new</span> AddUsersCommand { UsersStream = expectedStream }; |
|
1 |
<span id="lnum6" style="color: #606060"> 6:</span> <span style="color: #008000">// service with mocked unit of work</span> |
|
1 |
<span id="lnum7" style="color: #606060"> 7:</span> IServiceCommandHandler<AddUsersCommand> handler = <span style="color: #0000ff">new</span> AddUsersCommandHandler(mockUnit.Object); |
|
1 |
<span id="lnum8" style="color: #606060"> 8:</span> |
|
1 |
<span id="lnum9" style="color: #606060"> 9:</span> <span style="color: #008000">// act</span> |
|
1 |
<span id="lnum10" style="color: #606060"> 10:</span> handler.Handle(expectedCommand); |
|
1 |
<span id="lnum11" style="color: #606060"> 11:</span> |
|
1 |
<span id="lnum12" style="color: #606060"> 12:</span> <span style="color: #008000">// assertions against the command behaviour</span> |
|
1 |
<span id="lnum13" style="color: #606060"> 13:</span> mockUnit |
|
1 |
<span id="lnum14" style="color: #606060"> 14:</span> .Verify(x => x.BeginTransaction(), Times.Once()); |
|
1 |
<span id="lnum15" style="color: #606060"> 15:</span> mockUnit |
|
1 |
<span id="lnum16" style="color: #606060"> 16:</span> .Verify(x => x.Save(It.IsAny<User>())); |
|
1 |
<span id="lnum17" style="color: #606060"> 17:</span> mockUnit |
|
1 |
<span id="lnum18" style="color: #606060"> 18:</span> .Verify(x => x.CommitTransaction(), Times.Once()); |
|
1 |
<span id="lnum19" style="color: #606060"> 19:</span> } |
The second one is the (in)famous integration test, where you call the WCF service and test the overall process. Unfortunately for the second step I couldn’t find anything really TDD so I had to figure out by myself a different solution.
IIS Express
Yesterday I spent some time googling around for a solution while I found a nice post from Scott Gu about IIS express. With IIS express we have an in-process IIS server that we can startup or shutdown at any time during the lifecycle of our tests. It provides almost all the functionalities provided by IIS and it also trigger the Application_Start event, something not very well handled by Cassini …
So, first of all I had to figure out how to bootstrap my unit tests to run over a different IIS than the one configured in the web.config.
Piece of cake, first of all I need something that allows me to bootstrap IIS express before my unit test harness starts:
|
1 |
<span id="lnum1" style="color: #606060"> 1:</span> [ClassInitialize] |
|
1 |
<span id="lnum2" style="color: #606060"> 2:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">void</span> ClassInitialize(TestContext context) |
|
1 |
<span id="lnum3" style="color: #606060"> 3:</span> { |
|
1 |
<span id="lnum4" style="color: #606060"> 4:</span> <span style="color: #008000">// create a process for IIS express</span> |
|
1 |
<span id="lnum5" style="color: #606060"> 5:</span> issProcess = <span style="color: #0000ff">new</span> Thread(IssUtility.StartIisExpress) |
|
1 |
<span id="lnum6" style="color: #606060"> 6:</span> { |
|
1 |
<span id="lnum7" style="color: #606060"> 7:</span> IsBackground = <span style="color: #0000ff">true</span> |
|
1 |
<span id="lnum8" style="color: #606060"> 8:</span> }; |
|
1 |
<span id="lnum9" style="color: #606060"> 9:</span> <span style="color: #008000">// start it</span> |
|
1 |
<span id="lnum10" style="color: #606060"> 10:</span> issProcess.Start(); |
|
1 |
<span id="lnum11" style="color: #606060"> 11:</span> <span style="color: #008000">// create a new channel factory using the generated URL</span> |
|
1 |
<span id="lnum12" style="color: #606060"> 12:</span> factory = <span style="color: #0000ff">new</span> ChannelFactory<IDomainWriteService>( |
|
1 |
<span id="lnum13" style="color: #606060"> 13:</span> <span style="color: #0000ff">new</span> BasicHttpBinding(), |
|
1 |
<span id="lnum14" style="color: #606060"> 14:</span> <span style="color: #0000ff">new</span> EndpointAddress(<span style="color: #006080">"http://localhost:5555/services/DomainWriteService.svc"</span>)); |
|
1 |
<span id="lnum15" style="color: #606060"> 15:</span> } |
Then I need another utility to kill everything when my test harness is done:
|
1 |
<span id="lnum1" style="color: #606060"> 1:</span> [ClassCleanup] |
|
1 |
<span id="lnum2" style="color: #606060"> 2:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">void</span> ClassCleanup() |
|
1 |
<span id="lnum3" style="color: #606060"> 3:</span> { |
|
1 |
<span id="lnum4" style="color: #606060"> 4:</span> <span style="color: #008000">// close the WCF factory</span> |
|
1 |
<span id="lnum5" style="color: #606060"> 5:</span> factory.Close(); |
|
1 |
<span id="lnum6" style="color: #606060"> 6:</span> <span style="color: #008000">// shutdown IIS express</span> |
|
1 |
<span id="lnum7" style="color: #606060"> 7:</span> IssUtility.TearDownIis(); |
|
1 |
<span id="lnum8" style="color: #606060"> 8:</span> issProcess.Abort(); |
|
1 |
<span id="lnum9" style="color: #606060"> 9:</span> } |
At this point I can write any test in the following format:
|
1 |
<span id="lnum1" style="color: #606060"> 1:</span> [TestMethod] |
|
1 |
<span id="lnum2" style="color: #606060"> 2:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span> ChannelFactory_ReceiveValidCommand_WillExecute() |
|
1 |
<span id="lnum3" style="color: #606060"> 3:</span> { |
|
1 |
<span id="lnum4" style="color: #606060"> 4:</span> var expectedCommand = BuildAddUsersCommand(); |
|
1 |
<span id="lnum5" style="color: #606060"> 5:</span> service.AddUsers(expectedCommand); |
|
1 |
<span id="lnum6" style="color: #606060"> 6:</span> } |
I found this technique very useful because we work with TFS on Azure so my Build environment cannot be polluted at all with any third party components or by installing the WCF service under test in the build machine.
In the same way I found very quick to bootstrap IIS express for my tests, honestly they are faster than running on my local IIS 8.
IIS Express utility class
Below is the code I use to initialize IIS express, feel free to use it in your tests and let me know if you come up with a better solution than this one.
|
1 |
<span id="lnum1" style="color: #606060"> 1:</span> <span style="color: #0000ff">namespace</span> xxx |
|
1 |
<span id="lnum2" style="color: #606060"> 2:</span> { |
|
1 |
<span id="lnum3" style="color: #606060"> 3:</span> <span style="color: #0000ff">using</span> System.Diagnostics; |
|
1 |
<span id="lnum4" style="color: #606060"> 4:</span> |
|
1 |
<span id="lnum5" style="color: #606060"> 5:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span> IssUtility |
|
1 |
<span id="lnum6" style="color: #606060"> 6:</span> { |
|
1 |
<span id="lnum7" style="color: #606060"> 7:</span> <span style="color: #0000ff">private</span> <span style="color: #0000ff">static</span> Process iisProcess; |
|
1 |
<span id="lnum8" style="color: #606060"> 8:</span> |
|
1 |
<span id="lnum9" style="color: #606060"> 9:</span> <span style="color: #008000">// Start IIS on port 5555</span> |
|
1 |
<span id="lnum10" style="color: #606060"> 10:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">void</span> StartIisExpress() |
|
1 |
<span id="lnum11" style="color: #606060"> 11:</span> { |
|
1 |
<span id="lnum12" style="color: #606060"> 12:</span> var startInfo = <span style="color: #0000ff">new</span> ProcessStartInfo |
|
1 |
<span id="lnum13" style="color: #606060"> 13:</span> { |
|
1 |
<span id="lnum14" style="color: #606060"> 14:</span> WindowStyle = ProcessWindowStyle.Normal, |
|
1 |
<span id="lnum15" style="color: #606060"> 15:</span> ErrorDialog = <span style="color: #0000ff">true</span>, |
|
1 |
<span id="lnum16" style="color: #606060"> 16:</span> LoadUserProfile = <span style="color: #0000ff">true</span>, |
|
1 |
<span id="lnum17" style="color: #606060"> 17:</span> CreateNoWindow = <span style="color: #0000ff">false</span>, |
|
1 |
<span id="lnum18" style="color: #606060"> 18:</span> UseShellExecute = <span style="color: #0000ff">false</span>, |
|
1 |
<span id="lnum19" style="color: #606060"> 19:</span> Arguments = |
|
1 |
<span id="lnum20" style="color: #606060"> 20:</span> <span style="color: #0000ff">string</span>.Format(<span style="color: #006080">"/path:\"{0}\" /port:{1}"</span>, |
|
1 |
<span id="lnum21" style="color: #606060"> 21:</span> <span style="color: #006080">@"your WCF dev path"</span>, 5555) |
|
1 |
<span id="lnum22" style="color: #606060"> 22:</span> }; |
|
1 |
<span id="lnum23" style="color: #606060"> 23:</span> |
|
1 |
<span id="lnum24" style="color: #606060"> 24:</span> var programfiles = <span style="color: #0000ff">string</span>.IsNullOrEmpty(startInfo.EnvironmentVariables[<span style="color: #006080">"programfiles"</span>]) |
|
1 |
<span id="lnum25" style="color: #606060"> 25:</span> ? startInfo.EnvironmentVariables[<span style="color: #006080">"programfiles(x86)"</span>] |
|
1 |
<span id="lnum26" style="color: #606060"> 26:</span> : startInfo.EnvironmentVariables[<span style="color: #006080">"programfiles"</span>]; |
|
1 |
<span id="lnum27" style="color: #606060"> 27:</span> |
|
1 |
<span id="lnum28" style="color: #606060"> 28:</span> startInfo.FileName = programfiles + <span style="color: #006080">"\\IIS Express\\iisexpress.exe"</span>; |
|
1 |
<span id="lnum29" style="color: #606060"> 29:</span> |
|
1 |
<span id="lnum30" style="color: #606060"> 30:</span> <span style="color: #0000ff">try</span> |
|
1 |
<span id="lnum31" style="color: #606060"> 31:</span> { |
|
1 |
<span id="lnum32" style="color: #606060"> 32:</span> iisProcess = <span style="color: #0000ff">new</span> Process {StartInfo = startInfo}; |
|
1 |
<span id="lnum33" style="color: #606060"> 33:</span> |
|
1 |
<span id="lnum34" style="color: #606060"> 34:</span> iisProcess.Start(); |
|
1 |
<span id="lnum35" style="color: #606060"> 35:</span> iisProcess.WaitForExit(); |
|
1 |
<span id="lnum36" style="color: #606060"> 36:</span> } |
|
1 |
<span id="lnum37" style="color: #606060"> 37:</span> <span style="color: #0000ff">catch</span> |
|
1 |
<span id="lnum38" style="color: #606060"> 38:</span> { |
|
1 |
<span id="lnum39" style="color: #606060"> 39:</span> iisProcess.CloseMainWindow(); |
|
1 |
<span id="lnum40" style="color: #606060"> 40:</span> iisProcess.Dispose(); |
|
1 |
<span id="lnum41" style="color: #606060"> 41:</span> } |
|
1 |
<span id="lnum42" style="color: #606060"> 42:</span> } |
|
1 |
<span id="lnum43" style="color: #606060"> 43:</span> |
|
1 |
<span id="lnum44" style="color: #606060"> 44:</span> <span style="color: #008000">// kill everything</span> |
|
1 |
<span id="lnum45" style="color: #606060"> 45:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">void</span> TearDownIis() |
|
1 |
<span id="lnum46" style="color: #606060"> 46:</span> { |
|
1 |
<span id="lnum47" style="color: #606060"> 47:</span> iisProcess.CloseMainWindow(); |
|
1 |
<span id="lnum48" style="color: #606060"> 48:</span> iisProcess.Dispose(); |
|
1 |
<span id="lnum49" style="color: #606060"> 49:</span> } |
|
1 |
<span id="lnum50" style="color: #606060"> 50:</span> } |
|
1 |
<span id="lnum51" style="color: #606060"> 51:</span> } |
Enjoy!

Recent Comments