Saturday, August 27, 2011

Unit testing FluentNHibernate...


As I recently posted, I just got back to unit testing...
I thought of a small personal project to start practicing on, and got to work. My project consisted of using Fluent NHibernate, so I wanted to create unit tests to make sure my mappings were good.

I came across an old blog post of Ayende explaining that he created a base class that exports all your mappings into an in-memory sqlite db just for testing.
This concept seemed really good to me, so I tried it myself, but ran into some small problems while trying to configure it for Fluent NHibernate, so I will post the new version to ayende's class that works for FNH :

In case you don't have the SQLite providers yet, you can download them here.

This is the class :
public class InMemoryDatabaseTest : IDisposable
{
    private static Configuration configuration;
    private static ISessionFactory SessionFactory;
    protected ISession session { get; set; }

    public InMemoryDatabaseTest(Assembly assemblyContainingMapping)
    {
        SessionFactory = Fluently.Configure()
            .Database(SQLiteConfiguration.Standard.InMemory().ShowSql())
            .ProxyFactoryFactory(typeof(ProxyFactoryFactory))
            .Mappings(m => m.FluentMappings
                            .Add(assemblyContainingMapping)
                      )
            .ExposeConfiguration(x => configuration = x)
            .BuildSessionFactory();

        session = SessionFactory.OpenSession();

        SchemaExport export = new SchemaExport(configuration);
        export.Execute(true, true, false, session.Connection, null);
    }

    public void Dispose()
    {
        session.Dispose();
    }
}

And then all you have to do to test your mapping would be something like this :
[TestClass]
public class UserTests : InMemoryDatabaseTest
{
    public UserTests() : base(typeof(UserMapping).Assembly)
    { }

    [TestMethod]
    public void UserMapping_CanSaveAndLoadUser()
    {
        object id;

        using (var tx = session.BeginTransaction())
        {
            id = session.Save(new Dal.Entities.User()
            {
                Username = "unittest",
                Password = "unittest1234",
                Email = "unittest@gmail.com"
            });

            tx.Commit();
        }

        session.Clear();

        using (var tx = session.BeginTransaction())
        {
            var user = session.Get<Dal.Entities.User>(id);

            Assert.AreEqual(user.Username, "unittest");
            Assert.AreEqual(user.Password, "unittest1234");
            Assert.AreEqual(user.Email, "unittest@gmail.com");

            tx.Commit();
        }
    }
}

It's that easy! :)

Thanks Ayende!

Monday, August 15, 2011

Beginning Unit Testing...


I finally decided to get back into Unit Testing...

I have worked once in the past for a short period of time (only a couple of months) with unit testing, and my experience is good with it. The problem is that where I currently work, my team isn't really into it, and aren't to keen on trying neither.

Since I haven't used it in a while, it feels to me as if I lost my unit testing mojo, and need to learn everything from scratch again.
At first, I though the best way to start is to actually write unit tests for projects I already wrote. This isn't TDD at all, but it's still something, and I feel as if I'm getting the "infrastructure" ready for the rest of the process...

Another thing I just thought about doing today was giving a lecture at work to my fellow team mates on unit testing, hoping that they will see the good side to it, and they'll want to know a little more on the subject!

Sometimes the best way to learn is by teaching! :)

I started building a demo project that I will show as part of a presentation i'm working on to go with my lecture. The project is just a simple calculator.

I am gathering all the points I need to discuss on this lecture, but trying to keep it simple and as convincing as possible to people who don't think it's useful or don't know what it is yet. Seems like quite a challenge, but I'm up for it!


So far this is what I got : (these are the points that are going into my presentation)

Let's say we're building a new class as part of a winform that does some calculation. How do we usually go about testing this ?
We would press f5 (debug) and play around with the UI a bit to see that everything's working...

Why is this bad ?
1. We have to do this again and again every time we change a little piece of code since we can't be sure it still works.
2. If someone else on the team changes our code, we'll need to run the tests again.
3. Someone might change a piece of code, play around with the UI and still not know about special corner-cases we were looking to handle in a specific way.
4. Time consuming!
5. In this case it's a winform and might be easy to test manually, but if we're just building a web service for example, I usually see people building these funny "tester" projects that they can send some sample input and see what happens.

Why does writing Unit Tests make life easier ?
1. By writing unit tests we are writing code that will check our functionality instead of doing it manually.
2. We can run the tests every change we make, and this will be much faster then manually testing.
3. If we write the tests properly and cover all the code we wrote, then someone else can change the code around or add some other functionality with the knowledge he didn't screw our code over!
4. Just by looking at the unit tests we can spot the corner-cases we should look out for.

What do we test and what not to test ?
1. Test all public methods in a class. If the tests are written good enough, then the private methods will be covered within these tests.
2. Make sure you're not writing tests to check the .net framework - this means you don't need to go crazy and start writing tests for each .net method!
3. Tests shouldn't need configuration. You should always be able to run the tests immediately. This means you need to avoid having your tests writing out to log files, db, network streams, etc. Instead, create mocks of these interfaces so they don't actually do this during testing.


Well, I feel like I'm new to this as well, so I'm looking for some feedback on these points I have for my presentation so far...
During the presentation I also want to show the simple demo test project I created, and how to write basic tests for it. I will show common cases when changing code can cause different behavior that might not be spotted at first by manually testing...

Seeya'...

P.S - Anyone interested in learning more about unit testing should check out these blogs : Ayende & Roy Osherove