Freekshow

April 20, 2008

Unit Testing in Visual Studio 2008 – part 1

Filed under: .Net,Unit Testing — Freek Leemhuis @ 2:54 pm
Tags: , ,

Unit testing – does it need an introduction?

Unit tests can massively improve the maintainability of any application. Bugs are found right after they get introduced, and refactoring code is done with great confidence. As a consultant, I’m participating in many different development teams. In the last year or so I’ve done a number of audits and coaching sessions on client sites. One thing that has struck me is how different shops approach unit testing varies enormously. Some have been doing it for years, and are very well versed in it. Others are struggling to integrate it into their practices, and others still have not made any strides at all. So, in my experience, the practice of unit testing is not so ubiquitous as you might expect. I’ve also found that, as is writing software in general, writing good unit tests is hard to do. It requires insight and experience, and for those who start out it can be a frustrating experience. My first set of unit tests were a fragile bunch. Sometimes they would break by dozens at a time, other times they would break where the actual code would be running fine.

So the answer to the first question, ‘does it need an introduction’ would be yes, plenty of times it does! That’s why I’ve decided to dedicate a number of posts on the art of unit testing. This post is the first in the series, and here I’ll focus on the environment. I’ll introduce the build-in unit testing framework for Visual Studio 2008. In later posts I’ll talk more about test patters, design for testability, mocking and code coverage.

Introducing the Visual Studio 2008 testing framework

Microsoft has provided MS Test for unit testing in Visual Studio since version 2005. These tools were only available if you were running one of the Team system editions. Luckily, since then Microsoft has come around and for the 2008 version unit testing is also available in the Professional Edition (but not Standard Edition). Things like code coverage analysis remain limited to the Team Suite editions. Find a comparison of the features for different editions here.

First of all, you will need to add a separate Test Project to your solution. Unit test will not be stored between your source classes, but always kept in a separate test project. Select your solution and click on File, Add, New Project

Note that by default, a number of items are created in your test project.

They include a AuthoringTests.txt: a text file containing general information about testing, ManualTest1.mht: a template-type ManualTest for adding functional test descriptions to your project (I’ve never met anybody who did), and a blank unit test UnitTest1.cs. Most likely you will want to change the default settings, so you don’t have to go and manually delete these items. Luckily you can do that through the options menu by clearing the checkboxes displayed below.

We add a reference in the test project to the project we’re trying to test. We’re now ready to add a test. Let’s assume that we have a piece of code like the following:

partial void OnCompanyNameChanged()

       {

           if (CompanyName.Length > 20)

           {

               CompanyName = CompanyName.Substring(0, 20);

           }

       }

We’re using Linq to SQL and the Northwind database, where I’ve added a partial class for the Customer. In the partial class I can add validation code like the one above. The company name can contain only 20 characters, and while we’re validating things like that in the user interface, it’s good practice to validate it server-side as well. In this case, we’re not throwing an exception but simply take the first 20 characters if the name provided is longer than 20 characters.
Now we can add a test to the test project. Click on Add, New Item, Class and add the new class. For naming, there’s a number of naming conventions you can choose from. I usually name the test class as <ClassToTest>_Tests.cs, so in this case I’d name it NwindDataContext_Tests.cs.
In the test class, you can have a number of test methods, which can test one or more of the methods in the class under test. This way, you will have a 1:1 between your classes and your test classes. Of course, you can have (and often you’d want to have) more than 1 test method for each method.
Since we’ve added a plain class, we need to introduce the namespace for the testing framework:

using Microsoft.VisualStudio.TestTools.UnitTesting;

We have to define the test class and methods as public. We decorate the class with the [TestClass] attribute, and add a new testmethod. Methods are decorated with the [TestMethod] attribute.

[TestClass]

    public class Customer_Tests

    {

        [TestMethod]

        public void OnNameChanged_MoreThan20Chars_TakesFirst20Chars()

        {

            string testName = “abcdefghijklmnopqrstuvw”;

            Customer customer = new Customer();

            customer.CompanyName = testName;

            string expectedName = “abcdefghijklmnopqrs”;

            Assert.AreEqual(expectedName, customer.CompanyName,

                “OnNameChanging should take only first 20 characters”);

        }

    }

For the test methods I’m using the naming convention

<MethodUnderTest>_<StateInput>_<ExpectedResult>.

I’ve used another convention in Unit testing, in that I’ve set up an expected value and I’m comparing that with the actual value. If we then use Assert.AreEqual to compare the two, the framework infers that the first value is the expected result, and the second the actual result. The third parameter of Assert is used to specify a detailed description that is displayed in the event that the test fails.
If we run the test, (right-click, choose Run Test), we’ll see in the test result window that the test fails:

Double-clicking on the result brings up the details of the test run:

In the error message we see that the expected value differed from the actual value, and we see the description returned that we have provided in the test.
Of course, the reason why the test fails is a simple counting error: I’ve included only 19 characters in the test condition, so as the test results indicate, I’ve missed out the letter ‘t’.
Fixing this by appending the letter in the test method results in Pass:

We’ve seen a number of test windows, Clicking on the Test Menu reveal there are more testing related windows :

The Test View Window

The Test View Window is a list of all test methods in all test projects of the solution. From this window, you can make a selection of the tests you want to run, and then choose to run or to debug the selected tests.

If you have a large number of tests, as is the case here (it’s the Enterprise Library) you will sometimes want to filter the list. Here I’ve filtered on the search term ‘isolatedstorage’ to narrow down the list of test methods.

The Test List Editor

If you have a large number of tests, it is more convenient to partition the tests into separate groups. The Test List Editor is where you assign tests to particular groups.

For example, if I’m working on Caching, it can be convenient to move all tests related to caching to a separate list, so you can easily pick out a group of tests you want to run.
Personally, I don’t use the Test List Editor much. The grouping does not automatically reflect the physical grouping you’ll have in your test project, which can be confusing. Apart from that, if you’re coding it is my belief that it’s better to run all unit tests, and not a subset, since the coding might introduce an effect in the codebase that is covered by unit tests in other test lists.

The test results window.

We have already seen this window in the previous paragraphs. In this window, the results of your last run are displayed. The framework will keep by default the last 25 test runs, and you can select the results of a previous run from the dropdown box.

Back to the test

Okay, why have I not used the separate Unit test template to add a test? Well, I’ve wanted to show you the ‘bare bones’ of what makes up a unit test. If we select the template for a unit test, we get a wizard-style dialog that forces us to choose the code that we want to test. This is of course contrary to the Test Driven Development paradigm, where you want to write your tests before you write the code. Let’s run the wizard now and see where it takes us. Select New, Unit Test. You will see the following dialog displayed.

You can use the Settings button to call up the following window where you can name the new test classes and methods:


In this case, we have not changed the default names and end up with CustomerTests test class(that’s not too bad) and a OnCompanyNameChangedTest test method. This is not according to the naming convention we had in mind, so if you’re using this option beware to rename your methods so they express exactly what you’re intending to test.
In addition, there’s a fair amount of code that’s been generated in our test class. For starters, there’s this bit:

private TestContext testContextInstance;
///
///Gets or sets the test context which provides
///information about and functionality for the current test run.
///
public TestContext TestContext
{
  get
  {
  return testContextInstance;
  }
  set
  {
  testContextInstance = value;
  }
}

The TestContext has methods like TestDir (returns the path to the test folder) and, in the case of an ASP.Net test, RequestedPage(returns a reference to the aspx page). It’s nice to have these options, but in most test cases it’s a case of YAGNI and therefore clutter.
Next up: a region called Additional test attributes. This contains some very useful suggestions:

#region Additional test attributes
  //
  //You can use the following additional attributes as you write your tests:
  //
  //Use ClassInitialize to run code before running the first test in the class
  //[ClassInitialize()]
  //public static void MyClassInitialize(TestContext testContext)
  //{
  //}
  //
  //Use ClassCleanup to run code after all tests in a class have run
  //[ClassCleanup()]
  //public static void MyClassCleanup()
  //{
  //}
  //
  //Use TestInitialize to run code before running each test
  //[TestInitialize()]
  //public void MyTestInitialize()
  //{
  //}
  //
  //Use TestCleanup to run code after each test has run
  //[TestCleanup()]
  //public void MyTestCleanup()
  //{
  //}
  //
  #endregion

The content of this we’ll get to later. However, do we really want these instructions sitting in every test class? More clutter.
Finally, we get to the meat. There’s an actual test method generated:

  /// <summary>

        ///A test for OnCompanyNameChanged

        ///</summary>

        [TestMethod()]

        [DeploymentItem(“nwind.BLL.dll”)]

        public void OnCompanyNameChangedTest()

        {

            Customer_Accessor target = new Customer_Accessor();                           

            target.OnCompanyNameChanged();

            Assert.Inconclusive(“A method that does not return a value cannot be verified.”);

        }

We have a skeleton test method, and can start thinking how to rename and build this into the actual test(s) that we want to perform. Note that a [DeploymentItem] is assigned, which you really only need if you want to run your tests in a separate deployment folder.

So are my private parts exposed now?

I’m glad you noticed. This is a little trick that the framework has played in order to allow the testing of private and internal methods. It uses reflection to create a shadowed copy of the code under test, and runs the tests against that rather than against the actual code (Customer_Accessor rather than Customer). When using the New Unit Test wizard, this accessor assembly is automatically created, regardless of the existence of private members.

Now that we’ve explored the MS Test environment, we can dive into the testing itself. This I’ll save for the next post.


 

April 16, 2008

Using WCF WSHttpBinding without installing .Net framework 3.0

Filed under: .Net — Freek Leemhuis @ 9:38 pm

The naming of Winfx as .Net framework 3.0 has caused a lot of misunderstanding. Recently I had to link up some WCF services to a .Net 2.0 project. Fine, just upgrade to 3.0 you’d say; 3.0 is only adding some stuff, and not replacing anything in the 2.0 runtime, right? Well, in this case the project had a dependancy on a third party CMS system, and that particular vendor came out with the party line that I’ve seen used more often: we support 2.0, but not 3.0. What this really means is: we’re not up to speed on these new technologies, and we don’t know what 3.0 actually is, but since we’ve not got any experience with it, we can’t say that we support it. Fair enough.
So that left me wondering how to best deploy the WCF services. Of course, I could just use basicHTTPBinding and use plain web service references to generate proxies, but in this case the services had security requirements that are best covered by using certificates. You can configure WCF services with certificates if you use WSHttpBinding, but not when you use BasicHTTPBinding.

One way to solve this would be to use the WSE (Web Service Enhancements) library, but having used WCF I figured I’d try to see what needs to be installed for WCF to function properly without having the 3.0 framework installed. It turned out you can do this relatively easy by distributing the WCF dll’s with the solution.
I added the following dll’s to the solution:

System.Servicemodel.dll
System.Runtime.Serialization.dll
System.Identitymodel.dll
System.Identitymodel.Selectors.dll
SMDiagnostics.dll
Microsoft.Transactions.Bridge.dll

I also had to pull a number of tags that normally sit in the 2.0 Machine.Config file and place them in the web.config file.

<sectionGroup name="system.runtime.serialization" type="System.Runtime.Serialization.Configuration.SerializationSectionGroup, System.Runtime.Serialization, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
   <section name="dataContractSerializer" type="System.Runtime.Serialization.Configuration.DataContractSerializerSection, System.Runtime.Serialization, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
  </sectionGroup>

<sectionGroup name="system.serviceModel" type="System.ServiceModel.Configuration.ServiceModelSectionGroup, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
   <section name="behaviors" type="System.ServiceModel.Configuration.BehaviorsSection, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
   <section name="bindings" type="System.ServiceModel.Configuration.BindingsSection, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
   <section name="client" type="System.ServiceModel.Configuration.ClientSection, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
   <section name="comContracts" type="System.ServiceModel.Configuration.ComContractsSection, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
   <section name="commonBehaviors" type="System.ServiceModel.Configuration.CommonBehaviorsSection, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowDefinition="MachineOnly" allowExeDefinition="MachineOnly"/>
   <section name="diagnostics" type="System.ServiceModel.Configuration.DiagnosticSection, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
   <section name="extensions" type="System.ServiceModel.Configuration.ExtensionsSection, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
   <section name="machineSettings" type="System.ServiceModel.Configuration.MachineSettingsSection, SMDiagnostics, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowDefinition="MachineOnly" allowExeDefinition="MachineOnly"/>
   <section name="serviceHostingEnvironment" type="System.ServiceModel.Configuration.ServiceHostingEnvironmentSection, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
   <section name="services" type="System.ServiceModel.Configuration.ServicesSection, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
  </sectionGroup>
  <sectionGroup name="system.serviceModel.activation" type="System.ServiceModel.Activation.Configuration.ServiceModelActivationSectionGroup, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
   <section name="diagnostics" type="System.ServiceModel.Activation.Configuration.DiagnosticSection, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
   <section name="net.pipe" type="System.ServiceModel.Activation.Configuration.NetPipeSection, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
   <section name="net.tcp" type="System.ServiceModel.Activation.Configuration.NetTcpSection, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
  </sectionGroup>
and also this bit:

<system.serviceModel>
  <extensions>
  <behaviorExtensions>
  <add name=”persistenceProvider” type=”System.ServiceModel.Configuration.PersistenceProviderElement, System.WorkflowServices, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35″/>
  <add name=”workflowRuntime” type=”System.ServiceModel.Configuration.WorkflowRuntimeElement, System.WorkflowServices, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35″/>
  <add name=”enableWebScript” type=”System.ServiceModel.Configuration.WebScriptEnablingElement, System.ServiceModel.Web, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35″/>
  <add name=”webHttp” type=”System.ServiceModel.Configuration.WebHttpElement, System.ServiceModel.Web, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35″/>
  <add name=”Microsoft.VisualStudio.Diagnostics.ServiceModelSink.Behavior” type=”Microsoft.VisualStudio.Diagnostics.ServiceModelSink.Behavior, Microsoft.VisualStudio.Diagnostics.ServiceModelSink, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a”/>
  </behaviorExtensions>
  <bindingElementExtensions>
  <add name=”webMessageEncoding” type=”System.ServiceModel.Configuration.WebMessageEncodingElement, System.ServiceModel.Web, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35″/>
  <add name=”context” type=”System.ServiceModel.Configuration.ContextBindingElementExtensionElement, System.WorkflowServices, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35″/>
  </bindingElementExtensions>
  <bindingExtensions>
  <add name=”wsHttpContextBinding” type=”System.ServiceModel.Configuration.WSHttpContextBindingCollectionElement, System.WorkflowServices, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35″/>
  <add name=”netTcpContextBinding” type=”System.ServiceModel.Configuration.NetTcpContextBindingCollectionElement, System.WorkflowServices, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35″/>
  <add name=”webHttpBinding” type=”System.ServiceModel.Configuration.WebHttpBindingCollectionElement, System.ServiceModel.Web, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35″/>
  </bindingExtensions>
  </extensions>
</system.serviceModel>

 

Worked like a charm. One thing to keep in mind with this solution is that these tags are not allowed in but the Machine.Config AND your solutions web/app.config, which means that if you install the 3.0 or 3.5 framework on the server after deployment, you will have to take the tags out again.

April 5, 2008

Come see Jimmy Nilsson

Filed under: .Net,Domain Driven Design,Events,Reading — Freek Leemhuis @ 7:43 pm
Tags:

Great news: on 24th of april, the DotNed usergroup will host another top speaker and Domain Driven Design guru Jimmy Nilsson. Keep an eye on their website for the anouncement. Found out more about Jimmy on his blog. I highly recommend his excellent book Applying Domain Driven Design and Patterns.

Blog at WordPress.com.