Friday, January 28, 2011

xUnit.Net – Running the tests (TestCategory)

In my previous post I showed an example about converting a MSTest class to xUnit.Net, and now I want to provide a solution for converting MSTest TestCategory attribute to an equivalent implementation in xUnit.Net.

MSTest allowed us to run the test that belongs to an specific category, let’s see a solution on how this can be accomplished in xUnit.Net.

using Xunit;
using Xunit.Extensions;

namespace xUnitCustomizations
{
    [CustomTestClassCommand]
    public class TestClass
    {
        [Fact, TestCategory("Unit")]
        public void FastTest()
        {
            Debug.WriteLine("fast test executed");
            Assert.True(true);
        }

        [Fact, TestCategory("Integration")]
        public void SlowTest()
        {
            Thread.Sleep(5000);
            Debug.WriteLine("slow test executed");
            Assert.True(true);
        }
    }
}

Create TestCategory attribute
 
namespace Xunit.Extensions
{
    public class TestCategoryAttribute : TraitAttribute
    {
        public TestCategoryAttribute(string category)
        : base("TestCategory", category) { }
    }
    ...
}

CustomTestClassCommandAttribute attribute is used to indicate that a custom test runner will be used

public class CustomTestClassCommandAttribute : RunWithAttribute
{
    public CustomTestClassCommandAttribute() : base(typeof(CustomTestClassCommand)) { }
}

CustomTestClassCommand  is the class that implements ITestClassCommand and acts as the runner for the test fixture

public class CustomTestClassCommand : ITestClassCommand { // Delegate most of the work to the existing TestClassCommand class so that we // can preserve any existing behavior (like supporting IUseFixture<T>). readonly TestClassCommand cmd = new TestClassCommand(); #region ITestClassCommand Members public object ObjectUnderTest { get { return cmd.ObjectUnderTest; } } public ITypeInfo TypeUnderTest { get { return cmd.TypeUnderTest; } set { cmd.TypeUnderTest = value; } } public int ChooseNextTest(ICollection<IMethodInfo> testsLeftToRun) { return cmd.ChooseNextTest(testsLeftToRun); } public Exception ClassFinish() { return cmd.ClassFinish(); } public Exception ClassStart() { return cmd.ClassStart(); } public IEnumerable<ITestCommand> EnumerateTestCommands(IMethodInfo testMethod) { return cmd.EnumerateTestCommands(testMethod); } public bool IsTestMethod(IMethodInfo testMethod) { return cmd.IsTestMethod(testMethod); } public IEnumerable<IMethodInfo> EnumerateTestMethods() { string category; foreach (IMethodInfo method in cmd.EnumerateTestMethods()) { category = string.Empty; foreach (IAttributeInfo attr in method.GetCustomAttributes(typeof(TestCategoryAttribute))) category = attr.GetPropertyValue<string>("Value"); if (category.ToLower().Contains("unit")) // We can make this configurable yield return method; } } #endregion }


The Method public IEnumerable<IMethodInfo> EnumerateTestMethods() filters the tests methods by TestCategory's attribute Value, note that we can make this configurable so we can configure our CI server to run unit test as soon as there are changes in the repository to provide quick feedback and schedule (e.g. once a day) the execution slower test like integration or Web UI test.

Monday, January 3, 2011

xUnit.Net – Running the tests (ClassInitialize – ClassCleanup)

I started using xUnit.Net few weeks ago. My first question was how to do the things I was used to do in other testing frameworks like MSTest or NUnit, specially when using these frameworks not only for unit testing but for higher level tests like Selenium RC web tests. So far, the framework seems to be very good and extensible.

I am going to show some scenarios I have run into converting MSTest to xUnit.Net, having the following class

using Microsoft.VisualStudio.TestTools.UnitTesting;

[TestClass] public class AdminPageTest { static SeleniumWebTestContext test = new SeleniumWebTestContext(); [ClassInitialize()] public static void ClassInitialize() { new LoginPage(test).LoginUser(“maria”, “******”); } [ClassCleanup()] public static void ClassCleanup() { new AdminPage(test.Driver).Logout(); test.StopWebTestContext(); } [TestCategory("WebTest"), TestMethod] public void TestViewMyProfile() { var profileWindow = new AdminPage(test.Driver).SelectMyProfile(); Assert.IsTrue(profileWindow.CheckFirstName(“Maria”)); profileWindow.Close(); } [TestCategory("WebTest"), TestMethod] public void TestAdminSearchUser() { var userWindow = new AdminPage(test.Driver).SelectUserManagement(); userWindow.Search("Marcano Maria"); Assert.IsTrue(adminTab.VerifyEmail("my-email@domain.com")); } }

Note that SeleniumWebTestContext is holding information about Selenium RC and starts the server.

ClassInitialize – ClassCleanup / IUseFixture<T>

Sometimes we require sharing information among the methods defined in a class, for example in web tests we want to share the logged in the user information, execute different actions and validations and log out at the end:

using Xunit;

public class AdminPageTest : IUseFixture<LoggedUserContext> { SeleniumWebTestContext test; public AdminPageTest() { } public void SetFixture(LoggedUserContext usrContext) { test = usrContext.Test; } ... }


and the LoggedUserContext class will look like this:
public class LoggedUserContext : IDisposable
{    
    public SeleniumWebTestContext Test;    
    public LoggedUserContext()    
    {        
        Test = new SeleniumWebTestContext();        
        new LoginPage(Test).LoginUser(“maria”, “******”);    
    }    

    public void Dispose()    
    {       
        new AdminPage(Test.Driver).Logout();
        Test.StopWebTestContext();
    }
}