Posts Tagged ‘Unit testing’

Programmatically check Log4Net log

Monday, November 30th, 2009

Today I needed to create unit test that checked if the NHibernate query was generated optimally.

Good thing is that, in case of an inefficient query, NHibernate puts a warning using log4net:

log.Warn( "firstResult/maxResults specified with collection fetch; applying in memory!" );

I wanted something like this:

[Test]
public void FindUsersBy_QueryWithLimit_LimitsOnSQLSide()
{
    using (LogChecker logChecker
        = new LogChecker("NHibernate", Level.Warn))
    {
        // Execute query using NHibernate

        // ....

        Assert.IsEmpty(logChecker.Messages);
    }
}

The problem is that it’s not that easy to attach MemoryAppender for a duration of unit test to the specified logger.

Anyway here’s the code:

public class LogChecker : IDisposable
{
    readonly Logger _logger;
    readonly Level _previousLevel;
    readonly MemoryAppender _appender = new MemoryAppender();

    public LogChecker(string logName, Level levelToCheck)
    {
        _logger = (Logger)LogManager.GetLogger(logName).Logger;
        _logger.AddAppender(_appender);
        _previousLevel = _logger.Level;
        _logger.Level = levelToCheck;
    }

    public List<string> Messages
    {
        get
        {
            return new List<loggingEvent>(_appender.GetEvents())
                  .ConvertAll(x => x.RenderedMessage);
        }
    }

    public void Dispose()
    {
        _logger.Level = _previousLevel;
        _logger.RemoveAppender(_appender);
    }
};

Unit testing is good

Friday, October 23rd, 2009

Recently Ayende added another spot-the-bug post. Usually I’m just curious how fast people find the problem.

Some time ago he also published few posts about how he does unit testing.

In “Scenario driven tests” he says that, as I understand it, it’s better to test a whole scenario not a single method. I’m okay with that, as long as scenarios are in reasonable numbers and code coverage is high.

In “Tests have got to justify themselves” he states “I am not using TDD.”

Well if you were you would not have so many problems in 10 lines of code, period.
You want to test scenarios ok, but when you’ve simple method with lots of logic, you are going to make a mistake at some point. Test it!

public static string FirstCharacters(this string self, int numOfChars)
{
    if (self == null)
        return "";
    if (self.Length < numOfChars)
        return self;
    return self
        .Replace(Environment.NewLine, " ")
        .Substring(0, numOfChars - 3) + "...";
}

Each of the following tests will fail:
(negative values are not reasonable input for such method, so we won’t go there)

[Test]
public void FirstCharacters_EmptyString_Truncates()
{
    Assert.AreEqual("", "".FirstCharacters(0));
}

[Test]
public void FirstCharacters_RegularString_Truncates()
{
    Assert.AreEqual("12345", "12345".FirstCharacters(5));
}

[Test]
public void Method_Condition_Result()
{
    Assert.AreEqual("...", "12345".FirstCharacters(2));
}

[Test]
public void FirstCharacters_StringWithNewLine_Truncates()
{
    Assert.AreEqual("  ", "rnrnrn".FirstCharacters(2));
}

[Test]
public void FirstCharacters_StringWithNewLine_Truncates2()
{
    Assert.AreEqual(" ...", "nnnnnnnn".FirstCharacters(4));
}

[Test]
public void FirstCharacters_StringWithNewLine_Truncates3()
{
    Assert.AreEqual("start end", "startnend".FirstCharacters(255));
}

How to test email sending?

Friday, September 25th, 2009

Nearly every web application these days sends some notifications to its users.

Many times before, I came across people who said that it’s not possible to test such things.
Well, they are wrong!

There is a very cool project on sourceforge called nDumbster:
http://ndumbster.sourceforge.net

nDumbster is a simple fake SMTP server designed especially to enable unit testing.

[TestFixture]
public class SmtpClientTest
{
    private const int _port = 25;
    private SimpleSmtpServer _smtpServer;

    [SetUp]
    public void SetUp()
    {
        _smtpServer = SimpleSmtpServer.Start(_port);
    }

    [TearDown]
    public void TearDown()
    {
        _smtpServer.Stop();
    }

    [Test]
    public void SendMessage_SendsMessage()
    {
        Mail.Text("Some tex")
            .Subject("Some subject")
            .From(new MailBox("alice@mail.com", "Alice"))
            .To(new MailBox("bob@mail.com", "Bob"))
            .UsingNewSmtp()
            .Server("localhost")
            .OnPort(_port)
            .Send();

        Assert.AreEqual(1, _smtpServer.ReceivedEmailCount);
        SmtpMessage mail = _smtpServer.ReceivedEmail[0];
        Assert.AreEqual(""Alice" <alice@mail.com>", mail.Headers["From"]);
        Assert.AreEqual(""Bob" <bob@mail.com>", mail.Headers["To"]);
        Assert.AreEqual("Some subject", mail.Headers["Subject"]);
        Assert.AreEqual("Some text", mail.Body);
    }
};

GoToTest macro for VisualStudio

Wednesday, September 23rd, 2009

4

When you are doing Test Driven Development (TDD) you are constantly switching back and forth between production code and test classes.

As it’s good idea to have those files in separate projects, it’s sometimes very hard to find test code for a class and vice-versa.

Some time ago I decided to write a simple Visual Studio macro, that solves this problem.
It’s based on the convention that all your test classes have Test or Tests suffix (e.g. CSVReader.cs and CSVReaderTests.cs)

I’m far from being VB expert, so the code is not perfect (Why does VS use Visual Basic for Macros?):

Imports System
Imports EnvDTE
Imports EnvDTE80
Imports System.Diagnostics
Imports System.IO

Public Module MainModule
    Dim _patterns As String() = {"{0}Test", "{0}Tests"}
    Dim _reversePatterns As String() = {"Tests", "Test"}

    Sub GoToTest()
        If DTE.SelectedItems.Count = 0 Then
            Return
        End If

        Dim fullFileName As String = DTE.ActiveDocument.Name
        Dim fileName As String = Path.GetFileNameWithoutExtension(fullFileName)
        Dim extension As String = Path.GetExtension(fullFileName)

        If IsTestFile(fileName) Then
            For Each reversePattern As String In _reversePatterns
                If fileName.Contains(reversePattern) Then
                    TryOpen(fileName.Replace(reversePattern, "") + extension)
                End If
            Next
        Else
            For Each pattern As String In _patterns
                TryOpen(String.Format(pattern, fileName) + extension)
            Next pattern
        End If
    End Sub

    Function IsTestFile(ByVal fileName As String)
        For Each reversePattern As String In _reversePatterns
            If fileName.Contains(reversePattern) Then Return True
        Next
        Return False
    End Function

    Function TryOpen(ByVal fileName As String) As Boolean
        Dim item As EnvDTE.ProjectItem
        item = FindItem(FileName)
        If Not (item Is Nothing) Then
            OpenItem(item)
            Return True
        End If
        Return False
    End Function

    Function FindItem(ByVal fileName As String) As EnvDTE.ProjectItem
        If String.IsNullOrEmpty(fileName) Then
            Return Nothing
        End If
        Dim item As EnvDTE.ProjectItem = DTE.Solution.FindProjectItem(fileName)
        Return item
    End Function

    Sub OpenItem(ByVal item As EnvDTE.ProjectItem)
        item.Open()
        item.Document.Activate()
    End Sub

End Module

How to install:

Start Visual Studio and go to Tools/Macros/Load Macro Project…:
1

Right click on the Visual Studio toolbar, and select customize:
2

Select ‘Macros’ category and find ‘GoToTest’ Macro:
3

Of course you can change the name of the button.
4

And finally the macro itself:
GotoTestMacro

My ReSharper templates for Unit Testing

Monday, September 21st, 2009

As I always have problem synchronizing my office and home machine’s templates I thought this would be good place to store them.

Inline templates (LiveTemplates.xml)

test

[Test]
public void Method_Condition_Result()
{
	$END$
}

setup

[SetUp]
public void SetUp()
{
	$END$
}

record

using(mocks.Record())
{
	$END$
}

play

using(mocks.Playback())
{
	$END$
}

File templates (FileTemplates.xml)

NUnitTestFile

using NUnit.Framework;

namespace $Namespace$
{
    [TestFixture]
    public class $FileName$
    {
        [Test]
        public void Method_Condition_Result()
        {
        }
    };
}

FileTemplates
LiveTemplates