Showing posts with label Unit Tests. Show all posts
Showing posts with label Unit Tests. Show all posts

Friday, May 22, 2015

XUnit and MSBuild

Recently I needed to execute xUnit tests with MSBuild, so I've spent some time for creating a MSBuild project running the tests. Luckily xUnit already have MSBuild tasks so I just needed to hook it up.
I wanted to search for all xUnit unit test dlls inside a folder and run the tests there. So I'm searching for xunit.core.dll file to get all the folders eventually containing such dlls and then search all these folders for a pattern - *.Tests.dll. When the tests dlls are found xunit task is run for all of them. So here's the result .proj file:
<?xml version="1.0" encoding="utf-8"?>
<Project
    DefaultTargets="Test"
    xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

    <UsingTask
      AssemblyFile="xunit.runner.msbuild.dll"
      TaskName="Xunit.Runner.MSBuild.xunit"/>

    <UsingTask TaskName="GetAssemblies" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
        <ParameterGroup>
            <Path ParameterType="System.String" Required="true" />
            <XUnitFileName ParameterType="System.String" Required="false"/>
            <TestsSearchPattern ParameterType="System.String" Required="false"/>
            <Assemblies ParameterType="System.String[]" Output="true" />
        </ParameterGroup>
        <Task>
            <Code Type="Fragment" Language="cs">
                <![CDATA[
                    var xUnitFileName = XUnitFileName ?? "xunit.core.dll";
                    var testsSearchPattern = TestsSearchPattern ?? "*.Tests.dll";
                
                    // get all the directories containing xUnit dlls
                    var directories = System.IO.Directory.GetFiles(Path, xUnitFileName, System.IO.SearchOption.AllDirectories)
                        .Select(file => System.IO.Path.GetDirectoryName(file));

                    var assembliesList = new List<string>();

                    foreach(string directory in directories)
                    {
                        // get all test dlls from the given paths
                        assembliesList.AddRange(System.IO.Directory.GetFiles(directory, testsSearchPattern, System.IO.SearchOption.TopDirectoryOnly));
                    }

                    Assemblies = assembliesList.ToArray();
                ]]>
            </Code>
        </Task>
    </UsingTask>

    <Target Name="Test">
        <GetAssemblies Path="$(BuildRoot).">
            <Output PropertyName="TestAssemblies" TaskParameter="Assemblies"/>
        </GetAssemblies>
        <xunit Assemblies="$(TestAssemblies)" />
    </Target>
</Project>

Thursday, November 15, 2012

Testing private methods with Visual Studio

When writing unit tests in most of the cases we have to test private methods. Let's see how this can be achieved using Visual Studio. I will give you three options:

  1. You can test private methods using Reflection, but this is not always a good option, as when you change the name or input arguments of a Method, you won't get any error when building, but you will get exceptions run time.
    We will use the project from my previous post Getting console output within a unit test.
    We will add a private method to the DummyClass:
    using System;
    
    namespace ConsoleLogger
    {
        public class DummyClass
        {
            public void WriteToConsole(string text)
            {
                Console.Write(text);
            }
    
            private void PrivateWriteToConsole(string text)
            {
                Console.Write("Private: " + text);
            }
        }
    }
    
    Now we can add a unit test to test the method using reflection:
    [TestMethod]
    public void PrivateWriteToConsoleReflection()
    {
        var currentConsoleOut = Console.Out;
    
        DummyClass target = new DummyClass();
        Type type = typeof(DummyClass);
    
        string text = "Hello";
    
        using (var consoleOutput = new ConsoleOutput())
        {
            var method = type.GetMethod("PrivateWriteToConsole", 
                BindingFlags.NonPublic | BindingFlags.Instance);
            method.Invoke(target, new object[1] { text });
    
            Assert.AreEqual(string.Format("Private: {0}", text), 
                consoleOutput.GetOuput());
        }
    
        Assert.AreEqual(currentConsoleOut, Console.Out);
    }
    We get and invoke the method using reflection.

  2. Another options is to use PrivateObject class. Using it you can easily call private method, but you have the same problem, you won't get compile exception when name or parameters are changed. Here is the same test written using PrivateObject:
    [TestMethod]
    public void PrivateWriteToConsolePrivateObject()
    {
        var currentConsoleOut = Console.Out;
    
        PrivateObject target = new PrivateObject(typeof(DummyClass));
    
        string text = "Hello";
    
        using (var consoleOutput = new ConsoleOutput())
        {
            target.Invoke("PrivateWriteToConsole", text);
    
            Assert.AreEqual(string.Format("Private: {0}", text), 
                consoleOutput.GetOuput());
        }
    
        Assert.AreEqual(currentConsoleOut, Console.Out);
    }
    
  3. And here is the third option which I think is the best. You can add .accessor file containing the name of the assembly whose private methods you want to see, or you can use Visual Studio "Create Unit Tests..." wizard to do this for you:

    this will add the following unit test:
    /// 
    ///A test for PrivateWriteToConsole
    ///
    [TestMethod]
    [DeploymentItem("ConsoleLogger.exe")]
    public void PrivateWriteToConsoleTest()
    {
        DummyClass_Accessor target = new DummyClass_Accessor(); // TODO: Initialize to an appropriate value
        string text = string.Empty; // TODO: Initialize to an appropriate value
        target.PrivateWriteToConsole(text);
        Assert.Inconclusive("A method that does not return a value cannot be verified.");
    }
    
    we will modify it for our needs:
    /// 
    ///A test for PrivateWriteToConsole
    ///
    [TestMethod]
    [DeploymentItem("ConsoleLogger.exe")]
    public void PrivateWriteToConsoleTest()
    {
        var currentConsoleOut = Console.Out;
    
        DummyClass_Accessor target = new DummyClass_Accessor();
    
        string text = "Hello";
    
        using (var consoleOutput = new ConsoleOutput())
        {
            target.PrivateWriteToConsole(text);
    
            Assert.AreEqual(string.Format("Private: {0}", text),
                consoleOutput.GetOuput());
        }
    
        Assert.AreEqual(currentConsoleOut, Console.Out);
    }
    
    When Visual Studio builds the project it will generate ConsoleLogger_Accessor.exe assembly, containing DummyClass_Accessor class with public methods only.
  4. Just to mention that you can test internal methods using the same approaches, but you will have one more option - in the AssemblyInfo file of the assembly being tested, you can add  InternalsVisibleTo attribute to specify which assembly will see the internal methods, in our case:
    [assembly: InternalsVisibleTo("ConsoleLogger.Tests")]
    
    now we will add an internal method to the same class:
    using System;
    
    namespace ConsoleLogger
    {
        public class DummyClass
        {
            public void WriteToConsole(string text)
            {
                Console.Write(text);
            }
    
            private void PrivateWriteToConsole(string text)
            {
                Console.Write("Private: " + text);
            }
    
            internal void InternalWriteToConsole(string text)
            {
                Console.Write("Internal: " + text);
            }
        }
    }
    
    and here is the working test method:
    [TestMethod]
    public void InternalWriteToConsoleTest()
    {
        var currentConsoleOut = Console.Out;
    
        DummyClass target = new DummyClass();
    
        string text = "Hello";
    
        using (var consoleOutput = new ConsoleOutput())
        {
            target.InternalWriteToConsole(text);
    
            Assert.AreEqual(string.Format("Internal: {0}", text),
                consoleOutput.GetOuput());
        }
    
        Assert.AreEqual(currentConsoleOut, Console.Out);
    }
    
And here's the code - ConsoleLogger.zip

Getting console output within a unit test

Today I needed to test a method which writes to the Console to validate the ouput. It is not hard to change the default console output and check the result. However you may forget to return the original output at the end. So let's take a look at my solution.

Let say we have the following class we want to test:
using System;

namespace ConsoleLogger
{
    public class DummyClass
    {
        public void WriteToConsole(string text)
        {
            Console.Write(text);
        }
    }
}

I have created a small helper class to redirect the output to a StringWriter:
using System;
using System.IO;

namespace ConsoleLogger.Tests
{
    public class ConsoleOutput : IDisposable
    {
        private StringWriter stringWriter;
        private TextWriter originalOutput;

        public ConsoleOutput()
        {
            stringWriter = new StringWriter();
            originalOutput = Console.Out;
            Console.SetOut(stringWriter);
        }

        public string GetOuput()
        {
            return stringWriter.ToString();
        }

        public void Dispose()
        {
            Console.SetOut(originalOutput);
            stringWriter.Dispose();
        }
    }
}
Now let's write the unit test:
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace ConsoleLogger.Tests
{
    [TestClass]
    public class DummyClassTest
    {
        [TestMethod]
        public void WriteToConsoleTest()
        {
            var currentConsoleOut = Console.Out;

            DummyClass target = new DummyClass(); 
            
            string text = "Hello";

            using (var consoleOutput = new ConsoleOutput())
            {
                target.WriteToConsole(text);

                Assert.AreEqual(text, consoleOutput.GetOuput());
            }

            Assert.AreEqual(currentConsoleOut, Console.Out);
        }
    }
}
This way we are sure that the original output will be restored and it's easy to get the output from the console.

You can find the sample here ConsoleLogger.zip.