开发者

unit test service layer - NUnit, NHibernate

I would like to unit test a DEPENDENT service layer which allows me to perform CRUD operation without mocking using NUnit. I know this is probably bad practice but I want to give it a try anyway - even if the tests have to run over night. My data is persisted using NHibernate and I have implemented a little library that 'bootstraps' the database which I could use in a [Setup] method. I am just wondering if someone has done something similar and what the fastest method for bootstrapping the database is. I am using something like this:

var cfg = new Configuration();
            cfg.Configure();
            cfg.AddAssembly("Bla");
            new SchemaExport(cfg).Execute(false, true, false);

to establish the db schema. After that I populate some lookup tables from some Excel tables.

Any feedback would be very much appreciated. Thanks.

Christian开发者_如何学C


Ayende has a good example of this approach in this blog post. His example is shown below with my comments.

As the Configuration is expensive to create, it is created only once per test run. A SQLite in-memory database is used as this is the fastest way to do queries.

public class InMemoryDatabaseTest : IDisposable
{
    private static Configuration Configuration;
    private static ISessionFactory SessionFactory;
    protected ISession session;

    public InMemoryDatabaseTest(Assembly assemblyContainingMapping)
    {
        if (Configuration == null)
        {
            Configuration = new Configuration()
                .SetProperty(Environment.ReleaseConnections,"on_close")
                .SetProperty(Environment.Dialect, typeof (SQLiteDialect).AssemblyQualifiedName)
                .SetProperty(Environment.ConnectionDriver, typeof(SQLite20Driver).AssemblyQualifiedName)
                .SetProperty(Environment.ConnectionString, "data source=:memory:")
                .SetProperty(Environment.ProxyFactoryFactoryClass, typeof (ProxyFactoryFactory).AssemblyQualifiedName)
                .AddAssembly(assemblyContainingMapping);

            SessionFactory = Configuration.BuildSessionFactory();
        }

        session = SessionFactory.OpenSession();

        new SchemaExport(Configuration).Execute(true, true, false, true, session.Connection, Console.Out);
    }

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

When using this, each test starts by creating required data.

public class BlogTestFixture : InMemoryDatabaseTest
{
    public BlogTestFixture() : base(typeof(Blog).Assembly)
    {
    }

    [Fact]
    public void CanSaveAndLoadBlog()
    {
        object id;

        using (var tx = session.BeginTransaction())
        {
            id = session.Save(new Blog
            {
                AllowsComments = true,
                CreatedAt = new DateTime(2000,1,1),
                Subtitle = "Hello",
                Title = "World",
            });

            tx.Commit();
        }

        session.Clear();


        using (var tx = session.BeginTransaction())
        {
            var blog = session.Get<Blog>(id);

            Assert.Equal(new DateTime(2000, 1, 1), blog.CreatedAt);
            Assert.Equal("Hello", blog.Subtitle);
            Assert.Equal("World", blog.Title);
            Assert.True(blog.AllowsComments);

            tx.Commit();
        }
    }
}


When writing such Integration Tests, the most important things to keep in mind that you should still

  • automate as much as possible (preferrably everything), which includes setting up the database and deleting it again after use
  • avoid the General Fixture antipattern, which means that each test case should start with an empty database, and itself fill in appropriate rows using Back Door Manipulation.

I have written about my own experiences with testing against databases several times in the past.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜