【问题标题】:Repository Pattern, NHibernate and Unit Testing存储库模式、NHibernate 和单元测试
【发布时间】:2011-03-19 17:19:35
【问题描述】:

我正在使用存储库模式开发 ASP MVC 3 项目,我想知道我们必须如何为存储库(聚合)上的 CRUD 操作编写单元测试。我被保存部分卡住了,我想回滚我的保存,从数据库中删除它,比如 Transaction.rollback,它对我不起作用,任何人都可以帮我提供一些示例代码来说明如何执行此操作。

这是我的代码概述

Begintransaction
session.save(Parent parent) in repository 
transaction.commit.

单元测试:

opensession;
_rep.Save(parent);

实际上,如果有一种方法或一段代码可以放入我的测试类中的 TearDown 部分,该部分在每次测试后运行就是,因为这在每次测试之后运行,当它在其他测试方法(删除、全部获取)等之后尝试做同样的事情时,我不知道如何处理它,我都很困惑。

【问题讨论】:

    标签: nhibernate nunit repository-pattern


    【解决方案1】:

    首先,您应该决定它是要执行的经典单元测试还是涉及数据库的集成测试。如果您想测试您的实体,您应该使用注入测试类的假存储库或模拟存储库(IOC 容器会很好)。因此不需要回滚(因为不涉及数据库)。如果您执行集成测试,那么我建议您使用内存数据库中的 Sqlite。它真的很轻。 Sqlite 的一个问题是它不提供事务支持。

    【讨论】:

      【解决方案2】:

      如果你想测试你的数据库访问,你可以使用像 SQLite 这样的内存数据库进行测试。当您的测试完成时,该数据库将消失,您无需清理测试数据。另一个优势是速度。内存数据库比 SQL Server 等快得多。

      【讨论】:

        【解决方案3】:

        我想您需要回滚以保持数据库清洁并避免测试之间的干扰。您可以通过为每个单元测试提供自己的数据库来实现这一点。在 Sql Server 精简版上有一篇很好的文章如何有效地实现这一点:http://www.differentpla.net/content/2009/03/using-sql-server-compact-edition-unit-testing

        编辑:一些例子:http://nhdatabasescopes.codeplex.com/.

        【讨论】:

        • 我们尝试了 SQL server CE 并且遇到了很多兼容性问题(CE 不支持某些功能)。现在我们使用 SQLite 没有问题。
        • 同样的事情可以用 SQLLite 来完成。此处描述:jasondentler.com/blog/2009/08/…。所描述的方法的缺点是每次实例化 SQLiteDatabaseScope 时都必须构建数据库......这需要很多时间。
        • 我们正在使用一个专用的测试会话工厂,它是为我们的测试创建的,并一直保存到所有测试运行(使用自定义提供程序,它会覆盖关闭连接方法而不做任何事情)。
        • 那么你需要回滚来隔离测试:)。当您为每个单元测试创​​建一个数据库时,您不再需要它,但执行测试需要更多时间。第一个链接解决了这两个问题,但仅适用于 SqlServer CE。同样的想法(在一个主文件中创建数据库,然后为每个执行的测试复制这个文件)也可以用于 SQLite。解决方案仍然足够快。
        • 非常感谢大家的回复。那么CE和lite哪个更好用,你能给我一些示例代码的链接吗?
        【解决方案4】:

        在您的 NUnit Setup 和 TearDown 方法中,您需要在数据库中删除并创建指定的表(使用脚本)。因此,在每次测试运行之前和之后,您的所有数据当然都会被删除,只留下空表供您测试。

        下面的例子使用的是MySql数据库,但即使是其他数据库,代码也应该差不多:

        public class DatabaseCleanup
            {
                private string _connectionString;
                private MySqlConnection _mySqlConnection;
                private string _filePath;
                public DatabaseCleanup()
                {
                    _filePath = @"C:\createdatabase.sql";
                    _connectionString = @"Server=localhost;Port=3306;Database=dbname;Uid=username;Password=password;";
                    _mySqlConnection = new MySqlConnection(_connectionString);
                }
        
                public void Create()
                {
                    string script = File.ReadAllText(_filePath);
                    ExecuteScript(script);
                }
        
                private void ExecuteScript(string script)
                {
                    try
                    {
                        MySqlCommand command = new MySqlCommand(script, _mySqlConnection);
                        _mySqlConnection.Open();
                        command.ExecuteNonQuery();
                        _mySqlConnection.Close();
                    }
                    catch (Exception exception)
                    {
                        throw;
                    }
                    finally
                    {
                        _mySqlConnection.Close();
                    }
                }
            }
        

        上述代码中提到的脚本createdatabase.sql 只是一个包含drop table 和create table 语句的sql 文件,您可以根据您的数据库提供这些语句。以下只是一个示例:

        DROP TABLE IF EXISTS `my_table`;
        
        CREATE TABLE `my_table` (
          `Id` varchar(50) NOT NULL,
          `Name` varchar(50) NOT NULL,
          PRIMARY KEY (`Id`)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
        

        然后您可以在 NUnit 测试类的 Setup 和 TearDown 方法中使用 DatabaseCleanup 类:

        DatabaseCleanup databaseCleanup = new DatabaseCleanup();
        databaseCleanup.Create();
        

        注意不要在生产环境中运行此数据库清理代码。仅在开发和测试环境中运行此类测试。

        P.S.,我不喜欢人们在这里说要使用像 SqlLite 这样的内存数据库。这完全违背了集成测试的全部目的。这意味着在您的开发环境中使用不同的数据库测试您的代码,然后在生产服务器上的不同数据库平台上进行部署;然后面对一场噩梦!

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-08-01
          • 2013-01-10
          • 1970-01-01
          • 1970-01-01
          • 2012-06-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多