Monday, October 11, 2010

Friday, October 1, 2010

Howto create unit test for privat, internal, and friend methods

Problem: You have a class with a private method that you wish to test.
public class ClassUnderTest
{
  private int DoSomePrivateStuff()
  {
     // Something is happening here
  }
} 
Since the method is private you can not access the it from the outside of the object.

How I solved this earlier was to make a testable class that inherited from the class I wanted to test.
public class TestableClassUnderTest : ClassUnderTest
{
  public int DoSomePrivateStuff()
  {
     base.DoSomePrivateStuff();
  }
} 

I now could do the following.
[TestClass]
public class ClassUnderTestTests
{
  [TestMethod]
  public void DoSomePrivateStuff_WhenCalled_ReturnsZero()
  {
     //Arrange
     var testClass = new TestableClassUnderTest();
     //Act
     var actual = testClass.DoSomePrivateStuff();
     //Assert
     Assert.AreEqual(0, actual);
  }
}

This is the classic Extract and Override pattern and there is nothing wrong with it.
But as a colleague showed me today, there is another way when you are using Visual Studio.
  1. Goto the ClassUnderTest in visual studio and right click. Select "Create Private Accessor" and select the test project you want this accessor in.
  2. Go to the test project you choose in step 1. You will now have a project folder called "Test References" with one file ending with ".accessor".
And that's it. VS have now created a class for you with the name "_accessor" that you can use in your tests. My example from above can now be rewritten to the following:
[TestClass]
public class ClassUnderTestTests
{
  [TestMethod]
  public void DoSomePrivateStuff_WhenCalled_ReturnsZero()
  {
     //Arrange
     var testClass = new ClassUnderTest_accessor();
     //Act
     var actual = testClass.DoSomePrivateStuff();
     //Assert
     Assert.AreEqual(0, actual);
  }
}
What's nice about this is that you don't need to create a bunch of testable classes. They are automagically created with reflection for you. Now you got more time to do fun stuff.... :-)

You can read more about this here: http://msdn.microsoft.com/en-us/library/bb385974.aspx

Share:

Wednesday, September 29, 2010

Testing exceptions in unit test

NUnit has a nice feature (Assert.Throws) that makes it possible to assert that a exception is thrown inside some code.

Visual Studio Unit Testing Framework seem to miss this feature and to check that the correct exception is thrown you would could use the following code:
[TestMethod]
public void WithTryCatch()
{
    // Arrange
    ApplicationException actualException = null;

    // Act
    try
    {
        ThrowSomeException();
    }
    catch (ApplicationExceptionex)
    {
        actualException = ex;
    }

    // Assert
    Assert.IsNotNull(actualException);
    Assert.AreEqual("Message", actualException.Message);
}

ExpectedExceptionAttribute
Using the ExpectedExceptionAttribute is possible. In the example below you expect a exception of type ApplicationException with the message set to "ExceptionMessage". All derived exceptions from ApplicationException will also satisfy the test.
[TestMethod]
[ExpectedException(typeof(ApplicationException), "ExceptionMessage", true)]
public SomeTest()
{
    DoSomething();
}

ExceptionAssert
The ExpectedExceptionAttribute could be enough in many situations. But it will not make you able to test specific attributes on an exception or the value of its inner exception. So why not make things it a little easier and more flexible with a helper class.
[TestMethod]
public void WithHelperClass()
{
    // Arrange
    // Act
    // Assert
    ExceptionAssert.Throws<ApplicationException>(
        () => ThrowSomeException(),
        ex => Assert.AreEqual("Message", ex.Message));
}
The Throws method catches the exception specified and will run the asserts on it. If no (or wrong) exception is thrown the test will fail.

The source code for the helper class can be found below.
[DebuggerStepThrough]
public static class ExceptionAssert
{
 /// 
 /// Asserts that an exception of type T is not thrown
 /// 
 /// >Typeparam name="T">Exception to look for
 /// Action to execute
 public static void DoesNotThrow<T>(Action action) where T : Exception
 {
  if (action == null)
  {
   throw new ArgumentNullException("action");
  }

  Exception actualException = null;
  try
  {
   action();
  }
  catch (T ex)
  {
   actualException = ex;
  }

  if (actualException != null)
  {
   throw new AssertFailedException(String.Format(
    "ExceptionAssert.DoesNotThrow failed. Exception <{0}> thrown with message <{1}>",
    actualException.GetType().FullName,
    actualException.Message));
  }
 }

 /// 
 /// Asserts that an exception is not thrown
 /// 
 /// Action to execute
 public static void DoesNotThrow(Action action)
 {
  DoesNotThrow(action);
 }

 /// 
 /// Asserts that an exception of type T is thrown
 /// 
 /// Exception to look for
 /// Action to execute
 public static void Throws<T>(Action action) where T : Exception
 {
  if (action == null)
  {
   throw new ArgumentNullException("action");
  }

  Exception actualException = null;
  try
  {
   action();
  }
  catch (Exception ex)
  {
   actualException = ex;
  }

  ValidateThrownException<T>(actualException, null);
 }

 /// 
 /// Asserts that an exception of type T is thrown
 /// 
 /// Exception to look for
 /// Action to execute
 /// Additional assert to be made on the exception
 public static void Throws<T>(Action action, Action<T> asserts) where T : Exception
 {
  if (action == null)
  {
   throw new ArgumentNullException("action");
  }

  Exception actualException = null;
  try
  {
   action();
  }
  catch (Exception ex)
  {
   actualException = ex;
  }

  ValidateThrownException(actualException, asserts);
 }

 /// 
 /// Asserts that an exception of type T is thrown
 /// 
 /// Exception to look for
 /// Action to execute
 /// Additional assert to be made on the exception
 /// Cleanup action to be executed.
 public static void Throws<T>(Action action, Action<T> asserts, Action finalAction) where T : Exception
 {
  if (action == null)
  {
   throw new ArgumentNullException("action");
  }

  Exception actualException = null;
  try
  {
   action();
  }
  catch (Exception ex)
  {
   actualException = ex;
  }
  finally
  {
   if (finalAction != null)
   {
    finalAction();
   }
  }

  ValidateThrownException(actualException, asserts);
 }

 /// 
 /// Valdidates the exception
 /// 
 /// Exception type to look for
 /// Exception to validate
 /// Additional asserts to be made on the exception
 private static void ValidateThrownException<T>(Exception actualException, Action<T> asserts) where T : Exception
 {
  if (actualException is T)
  {
   if (asserts != null)
   {
    asserts(actualException as T);
   }
  }
  else if (actualException == null)
  {
   throw new AssertFailedException(String.Format(
    "ExceptionAssert.Throws failed. No exception was thrown. Expected <{0}>.",
    typeof(T).FullName));
  }
  else
  {
   throw new AssertFailedException(String.Format(
    "ExceptionAssert.Throws failed. Expected <{0}>. Actual <{1}>",
    typeof(T).FullName,
    actualException.GetType().FullName));
  }
 }
}

Share:

Wednesday, September 15, 2010

Thursday, June 17, 2010

Variable number of arguments

The params keyword is very handy when you don't know the number of arguments is variable.
Note! No additional parameters are permitted after the params keyword.

// This method can take a variable number of ints
public int Sum(params int[] values)
{
  int sum = 0;
  foreach(int value in values)
  {
    sum += value;
  }
  return sum;
}

// Method calls
int sum1 = Sum(1, 2, 3, 4);
int sum2 = Sum(1, 2);

But be aware that this comes with a performance cost. When this method is called an array must be created, which is a costly operation.

If you know that your code for most of the time is calling this method with 3 arguments, then make a override with only three arguments. This method call is much quicker.
// Override with three params
public int Sum(int val1, int val2, int val3) { /* ... */ }
Share:

Monday, June 7, 2010

AD LDS for Windows 7

On XP machines there was a a service called ADAM that could be used when you needed a lightweight ActiveDirectory. This was extremely useful when developing applications that used AD. You could test your application without the need to modify your client/company AD.

But this service was not supported in Vista or Win7.

But now Microsoft have released AD LDS for Win7 that can be downloaded here:

http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=a45059af-47a8-4c96-afe3-93dab7b5b658
Share:

Wednesday, March 3, 2010

Comandline argument parser

Class that parses the commandline arguments.

using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;

namespace TestArgumentParser
{
    public class ArgumentParser
    {
        public string QuoteChars { get; set; }
        public string ValueSeparatorChars { get; set; }
        public string PrefixChars { get; set; }
        public Dictionary Params = new Dictionary();

        public string this[string key] { get{ return Params[key]; } }

        public ArgumentParser()
        {
            SetDefaultValues();
            string argString = GetComandLineArguments();
            Parse(argString);
        }

        public ArgumentParser(string args)
        {
            SetDefaultValues();
            Parse(args);
        }

        private static string GetComandLineArguments()
        {
            string argString = Environment.CommandLine;
            argString = argString.Replace("\"" + Process.GetCurrentProcess().MainModule.FileName + "\"", "");
            return argString;
        }

        public void Parse(string arguments)
        {
            string currentParam = string.Empty;
            string currentValue = string.Empty;
            bool readingParam = false;
            bool readingValue = false;
            bool startQuotes = false;
            foreach (char c in arguments)
            {
                if (IsPrefix(c))
                {
                    HandlePrefix(Params, ref currentParam, ref currentValue, ref readingParam);
                    continue;
                }
                if (readingParam)
                {
                    HandleParam(ref currentParam, ref readingParam, ref readingValue, c);
                    continue;
                }
                if (readingValue)
                {
                    HandleValue(ref currentValue, ref startQuotes, c);
                    continue;
                }

            }
            if (!string.IsNullOrEmpty(currentParam))
            {
                Params.Add(currentParam, currentValue);
            }
        }

        private void SetDefaultValues()
        {
            QuoteChars = "\"\'";
            ValueSeparatorChars = ":= ";
            PrefixChars = "-/";
        }

        private void HandlePrefix(Dictionary list, ref string currentParam, ref string currentValue, ref bool readingParam)
        {
            if (!string.IsNullOrEmpty(currentParam))
            {
                list.Add(currentParam, currentValue);
            }
            currentParam = string.Empty;
            currentValue = string.Empty;
            readingParam = true;
        }

        private void HandleValue(ref string currentValue, ref bool startQuotes, char c)
        {
            if (IsQuote(c))
            {
                startQuotes = !startQuotes;
                return;
            }
            if (!startQuotes && char.IsWhiteSpace(c))
            {
                return;
            }
            currentValue += c;
        }

        private void HandleParam(ref string currentParam, ref bool readingParam, ref bool readingValue, char c)
        {
            bool isValueSeparator = IsValueSeparator(c);
            if (!isValueSeparator)
            {
                currentParam += c;
            }
            else
            {
                readingValue = true;
                readingParam = false;
            }
        }

        private bool IsQuote(char c)
        {
            return QuoteChars.IndexOf(c) > -1;
        }

        private bool IsValueSeparator(char c)
        {
            return ValueSeparatorChars.IndexOf(c) > -1;
        }

        private bool IsPrefix(char c)
        {
            return PrefixChars.IndexOf(c) > -1;
        }
    }
}

Share: