What a challenge I had to try and create unit tests for workflows that is hosted by WCF services. This challenges took me an month to investigate and solve.

So, here I will explain and give code to show you how to do proper unit testing. I'm going under the assumption that you do know how to hook up a Workflow with WCF service. Otherwise just follow the code in the example project and you will quickly get it.

This post will show the basics first and later I will provide more posts of how to write unit test for different scenarios with ReceiveActivity and SendActivity.

First off, I created a simple project to illustrate the scenario to create a client object and post it to a service that has a workflow. The workflow is very basic that it will receive the Client object message and save it to a database. I will just fake the actually writing to the database.

The project structure:

 

















Firstly we define the ServiceContract IClientService and hook it up to the workflow CreateClientWorkflow.xoml ReceiveActivity.



























Next I created a custom WorkflowServiceHostFactory that I will explain later in another post. I removed the code behind for the ClientService.svc and made the following changes.
   1: <%@ ServiceHost Service="ClientWorkflowLibrary.CreateClientWorkflow, ClientWorkflowLibrary, Version=1.0.0.0, Culture=neutral"
   2:                 Factory="CommonLibrary.WorkflowServiceHostFactory, CommonLibrary, Version=1.0.0.0, Culture=neutral"  %>
This basically show some of the key setup points. Now for the main topic namely the Unit Test. The one unit test is straight forward that you start you service and create a service reference and use a client proxy to communicate with the service, but that is more of an integration test. I will show know the correct way to test your workflow service.
The Proper Unit Test Code
   1: [TestMethod]
   2: [Description("Run Unit Test with Administrator privlidges. (VS in Administrator mode)")]
   3: public void CreateClientWorflow_ReceiveCreateClient_ReturnClientWidthValidGuid()
   4: {
   5:     // Arrange
   6:     Client requestClient = new Client
   7:                                {
   8:                                    Id = Guid.Empty,
   9:                                    Name = "User",
  10:                                    Surname = "Nobody"
  11:                                };
  12:     Client responseClient = null;
  13:  
  14:     // Setup WCF and WF
  15:     Uri baseAddress = new Uri("http://127.0.0.1:8999/UnitTestHosting");
  16:     WorkflowServiceHost host = new WorkflowServiceHost(typeof(CreateClientWorkflow), baseAddress);
  17:  
  18:     host.AddServiceEndpoint(typeof(IClientService), new WSHttpContextBinding(), "CreateClient");
  19:  
  20:     ServiceMetadataBehavior smb = new ServiceMetadataBehavior { HttpGetEnabled = true };
  21:     host.Description.Behaviors.Add(smb);
  22:  
  23:     // Add Custom Services to Workflow runtime
  24:     WorkflowRuntimeBehavior runtime = host.Description.Behaviors.Find();
  25:     runtime.WorkflowRuntime.AddService(new ClientRepository());
  26:  
  27:     // To enable Workflow Tracking and Persistence
  28:     //string connectionString = "Data Source=(local);Initial Catalog=Workflow;Integrated Security=true";
  29:     //runtime.WorkflowRuntime.AddService(new SqlTrackingService(connectionString));
  30:     //runtime.WorkflowRuntime.AddService(new SqlWorkflowPersistenceService(connectionString));
  31:     
  32:     // Act
  33:     try
  34:     {
  35:         host.Open();
  36:         EndpointAddress address = new EndpointAddress("http://127.0.0.1:8999/UnitTestHosting/CreateClient");
  37:         IClientService service = ChannelFactory.CreateChannel(new WSHttpContextBinding(), address);
  38:  
  39:         responseClient = service.CreateClient(requestClient);
  40:  
  41:         host.Close();
  42:     }
  43:     catch (Exception e)
  44:     {
  45:         Assert.Fail(string.Format("Error: {0}", e));
  46:     }
  47:     finally
  48:     {
  49:         if (host.State != CommunicationState.Closed)
  50:         {
  51:             host.Abort();
  52:         }
  53:     }
  54:  
  55:     // Assert
  56:     Assert.IsNotNull(responseClient);
  57:     Assert.AreNotEqual(Guid.Empty, responseClient.Id);
  58:     Assert.AreEqual(requestClient.Name, responseClient.Name);
  59:     Assert.AreEqual(requestClient.Surname, responseClient.Surname);
  60: }
To be able to run this code you need Administrator privileges. I follow the AAA syntax in unit testing. The Arrange part is where I setup my requestClient object and the WCF and Workflow configuration.

Firstly you setup the Uri address of where you want to host the service that will be dynamically get set as the unit test is run. Then you create the WorkflowServiceHost and assign the workflow that you want to test. Add an Service endpoint with the ServiceContact that is used in the workflow and the appropriate binding that should be used. Finally a name for the endpoint that you will call later to test.

You can add additional behaviour metadata. When you instantiate a WorkflowServiceHost then the WorkflowRuntimeBehaviour is automatically added. In the code I do a find on the host Behaviours to return the WorkflowRuntimeBehaviour instance. This instance is required to add additional custom service. For example the ClientRepository.You will see the code comment if you would like to add Tracking and Persistence to you workflow under test.

Now for the Act part. With WorkflowServiceHost setup we can open a connection to the service. I create a channel to the ServiceContract IClientService and assign the endpoint address that should be queried.
I call the relevant method on the ServiceContract and close the host connection afterwards. 
There is a catch and finally section to make sure that no exceptions was raised and that the host was properly closed.

The Assert part is at the end to separate the asserts from any communication problems. I specify multiple asserts just to show the variety of unit tests that could be written and should actually be written to different unit tests.

Hope you enjoy this post and any feedback or recommendation is welcome!

Cheerio!

Source: ClientService 1.zip

Today I just want to rant a bit about how lazy the average C# Visual Studio developer really is. This is just to show how a few simple 5 points can be done to improve code and the quality of code.

Point 1: Install GhostDoc

Get GhostDoc it is free! Install it and assign the relevant hot key and start documenting your code. Methods, Interfaces and Properties. Even if it is private. This tool generate the necessary documentation structure. You just have to do little fix ups here and there in the documentations sometime. Not difficult to use!

With all your code documented, it is easy to generate the relevant help file of your code and it helps in intellisense to describe what the method and parameters are used for. You should even list all the exceptions that might be raised by the method in you documentation.

This help the developer that might use your method to be alerted of a possible exception and he will know how to handle it.

Point 2: Get Reharper or other similar tool

Invest in yourself! Be the best you can by buying a tool like Resharper that helps you to refactor quickly and write cleaner code. Development is your lively hood so do the best job.

Point 3: Use StyleCop practices

Another free tool. Even if you don’t use the tool explicitly, just try to apply some of the guidelines it provides. Just following some of them will make your code more readable and cleaner again.

Point 4: Run FxCop

Wow, another free tool! Just run this tool before you check in and you might be surprised at what you need to do to make your code comply with the .Net coding guidelines. Again this tool helps you to improve your code.

Point 5: Code Clean-up

In general just follow good code clean up practices. Remove commented code that is not used. Order you properties, public and private methods together. Follow consistent naming convention.

One big pain point is when I see developers clean up after them self in production code, but not in the unit tests that they have written. Please take care of both!

No More Points

Just try using these 5 points and you are already on a road to be a better Visual Studio developer and more productive. I just want the standard of code for Visual Studio developers to be lifted, but that is allot to ask for.

There are a whole lot of other tools to use, but the tool listed above is a good starting point and does not take allot of time to learn.

Any questions of feedback is welcome!

Cheerio!




I’ve read lately three very interesting articles about “Why OK not to Write Unit Tests”, “Selective Unit Testing - Costs and Benefits” and “It’s Okay to Write Unit Tests”.

All these articles have valid points, but needs to be take into context of the overall development practices.

Here is my 2 cents and why developers like to be spoon fed. Since people have been born they had to be spoon fed in live, food and education. You can’t deny it! You always needed help.

Now this is where unit tests come into play with production code where they help the developer to be spoon fed to understand the API of the system. And YES it is necessary, because if someone new joined the project someone has to guide and explain to him how every little bit of the system works. That could take a couple of days.

I would rather tell the newcomer to look at the unit tests, play with them and you will understand the API and  processes of the system. Everyone likes code, otherwise you are not a developer and get out!

That is my 2 cents to the whole discussion going on between the relevant articles.

Until next time!

Cheerio!




Today I would just like to show how to correctly write the code when using a WCF client proxy to call an WCF service.

Firstly you have to create a Service Reference to the service so that the relevant code is generated for the client proxy.

Here is the correct code:
   1: ProductClient client = new ProductClient();
   2: try
   3: {             
   4:    client.Open();
   5:    client.UpdatePrice(10, 100.50);
   6:    client.Close();
   7: }
   8: catch (Exception e)
   9: {
  10:    Assert.Fail(e.Message);
  11: }
  12: finally
  13: {
  14:    if (client.State != CommunicationState.Closed)
  15:    {
  16:       client.Abort();
  17:    }
  18: }

As you can see I first create the ProductClient proxy object and call open. Then the relevant Web method and then close the client proxy channel.

Then I create a catch section to display the relevant details. In the code above I use Assert.Fail() because this code is used in a unit test, but you can but logging in or any other exception handling logic.

The finally section is there to check if the channel has closed successfully otherwise will call abort on the channel. This allows the correct exception details been catch if an exception is raised in the web method or when the channel is closed.

Here is the wrong code (The popular approach):

   1: using(ProductClient client = new ProductClient())
   2: {

   3:     client.UpdatePrice(10, 100.50);
   4: }


So what is wrong with this? Well the problem is that if an exception is raised in the web method UpdatePrice() and an exception is raised in the Close() method of the client proxy channel is closed by the using() statement then the close exception would hide the original exception that is raised by the web method UpdatePrice().

The first code snippet is much clearer and show what really is happening if an exception should be raised. Where the using does make the code smaller and handle the open closing of the client proxy, but could hide the specific details of the original exception.


Cheerio!

Disqus