使用 Fluent nHibernate 进行单元测试 - 删除每个测试可能性的数据

发布于 2024-11-16 06:20:38 字数 4167 浏览 4 评论 0原文

我已经为 Fluent NHibernate 数据库设置了一些内存中 SQLite 单元测试,如下所示。效果很好。 (使用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();
        }
    }
}

现在,为了使用它,我只需继承InMemoryDatabase并添加我的测试方法,如下所示。

[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...
        }
    }

我的问题不是这不起作用。确实如此。但是,如果我在同一个类中有两个测试相似的数据,例如...

Username_Is_Unique(),然后是Email_Is_Unique()。又不是真正的测试,但这是一个很好的例子。

[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... 
}

我意识到这些都是非常糟糕的测试。这些不是真正的测试,我只是举一个例子。

在这两种情况下,我都会构造一个模拟 UserMember 或您拥有的东西并将其提交到数据库。

第一个工作正常,但由于数据库位于内存中(这是有道理的,因为我告诉它是这样),第二个则不然。实际上,单元测试并不反映现实世界的情况,因为每个测试都是独立的。但是,当按批处理顺序运行它们时,它的行为就像在现实世界中一样(我认为这在一定程度上是一件好事)

我想要做的是在每个方法之后刷新内存数据库。所以我想出了一个简单的方法来通过重复构造函数来做到这一点。它位于 InMemoryDatabase 类中。

protected void Restart() {
            SessionFactory = CreateSessionFactory();
            Session = SessionFactory.OpenSession();
            BuildSchema(Session);
        }

所以现在,在继承类的每个方法中,我在进行测试之前调用 Restart()

我觉得这不是解决我的问题的预期或有效的方法。谁能提出更好的解决方案?

如果有任何相关性,我正在使用 Fluent nHibernate 来实现持久性,并使用 Telerik JustMock 来进行模拟 - 但对于我的数据库内容,我还不需要任何嘲笑。

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

以酷 2024-11-23 06:20:38

您需要为每个测试删除并重新创建数据库。每个测试都应该独立于其他测试。您可以做两件事,首先让您的测试使用设置方法(假设这里使用 NUnit,但其他人具有相同的功能)

    [SetUp]
    public void Setup()
    {
        // Create in memory database
        Memdb = new InMemoryDatabase();
    }

或者,您可以将每个测试包装在数据库的 using 语句中。例如

[Test]
public void Test()
{
    using(var db = new InMemmoryDatabase())
    {
      Do Some Testing Here
    }
}

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)

    [SetUp]
    public void Setup()
    {
        // Create in memory database
        Memdb = new InMemoryDatabase();
    }

Alternatively, you can wrap each test in a using statement for the database. For example

[Test]
public void Test()
{
    using(var db = new InMemmoryDatabase())
    {
      Do Some Testing Here
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文