I have set up some In Memory SQLite Unit Tests for my Fluent NHibernate Database, which looks like this. It works fine. (Using NUnit)
namespace Testing.Database {
/// <summary>
/// Represents a memory only database that does not persist beyond the immediate
/// testing usage, using <see cref="System.Data.SQLite"/>.
/// </summary>
public abstract class InMemoryDatabase : IDisposable {
/// <summary>
/// The configuration of the memorized database.
/// </summary>
private static Configuration Configuration { get; set; }
/// <summary>
/// The singleton session factory.
/// </summary>
protected static ISessionFactory SessionFactory { get; set; }
/// <summary>
/// The current session being used.
/// </summary>
protected ISession Session { get; set; }
protected InMemoryDatabase() {
SessionFactory = CreateSessionFactory();
Session = SessionFactory.OpenSession();
BuildSchema(Session);
}
/// <summary>
/// Construct a memory based session factory.
/// </summary>
/// <returns>
/// The session factory in an SQLite Memory Database.
/// </returns>
private static ISessionFactory CreateSessionFactory() {
return FluentNHibernate.Cfg.Fluently.Configure()
.Database(FluentNHibernate.Cfg.Db.SQLiteConfiguration
.Standard
.InMemory()
.ShowSql())
.Mappings(mappings => mappings.FluentMappings.AddFromAssemblyOf<Data.Mappings.AspectMap>())
.ExposeConfiguration(configuration => Configuration = configuration)
.BuildSessionFactory();
}
/// <summary>
/// Builds the NHibernate Schema so that it can be mapped to the SessionFactory.
/// </summary>
/// <param name="Session">
/// The <see cref="NHibernate.ISession"/> to build a schema into.
/// </param>
private static void BuildSchema(ISession Session) {
var export = new NHibernate.Tool.hbm2ddl.SchemaExport(Configuration);
export.Execute(true, true, false, Session.Connection, null);
}
/// <summary>
/// Dispose of the session and released resources.
/// </summary>
public void Dispose() {
Session.Dispose();
}
}
}
So now, in order to use it, I just inherit InMemoryDatabase and add my Test methods, like this.
[TestFixture]
public class PersistenceTests : InMemoryDatabase {
[Test]
public void Save_Member() {
var member = // ...;
Session.Save(member); // not really how it looks, but you get the idea...
}
}
My problem isn’t that this doesn’t work. It does. But if I have two tests in the same class that test similar data, for instance …
Username_Is_Unique() and then Email_Is_Unique(). Not real tests again, but it’s a good example.
[Test]
public void Username_Is_Unique(){
var user = new User {
Name = "uniqueName"
Email = "uniqueEmail"
};
// do some testing here...
}
[Test]
public void Email_Is_Unique(){
var user = new User {
Name = "uniqueName"
Email = "uniqueEmail"
};
// do some testing here...
}
I realize these are very bad tests. These are not real tests, I am just citing an example.
In both cases, I would construct a mock User or Member or what-have you and submit it to the database.
The first one works fine, but since the database is in memory (which makes sense, since I told it to be), the second one doesn’t. Effectively, the Unit Tests do not reflect real-world situations, because each test stands alone. But when running them sequentially in a batch, it behaves like it should in the real world (I suppose that’s partially a good thing)
What I want to do is flush the in memory database after each method. So I came up with a simple way to do this by repeating the constructor. This goes in the InMemoryDatabase class.
protected void Restart() {
SessionFactory = CreateSessionFactory();
Session = SessionFactory.OpenSession();
BuildSchema(Session);
}
So now, in each method in my inheriting class, I call Restart() before I do my testing.
I feel like this isn’t the intended, or efficient way to solve my problem. Can anyone propose a better solution?
If it is of any relevance, I am using Fluent nHibernate for the persistence, and Telerik JustMock for my Mocking – but for my database stuff, I’ve yet to need any mocking.
You need to drop and recreate the database for every test. Every test should be independent of the other. You can do do two thing, first have your test use a setup method (Assuming NUnit here but others have the same functionality)
Alternatively, you can wrap each test in a using statement for the database. For example