代码之家  ›  专栏  ›  技术社区  ›  Kris van der Mast

单元测试数据访问层

  •  7
  • Kris van der Mast  · 技术社区  · 14 年前

    我一直在读一个项目的数据访问层单元测试。大多数选项归结为:

    • 使用专用的测试数据库,但在所有单元测试的最后阶段进行清理(或手动执行)
    • 使用数据库,但不要提交或只是回滚
    • 模拟数据库

    在以前的一个项目中,我们使用回滚方式,但我想知道更多关于其他选项的信息,以及如何最好地执行这些选项。如果你有样品/文章/视频/。。。请分享。

    4 回复  |  直到 14 年前
        1
  •  4
  •   n00begon Priidu Neemre    12 年前

    单元测试测试项目的一个方面,而不依赖于数据访问和表示。对于单元测试,您可以模拟数据库和用户依赖项注入,以将代码与数据提供程序解耦。这将导致更好的体系结构,并使您能够根据需要插入不同的数据提供程序。e、 从ADO.net到nHibernate。

    集成测试是您测试整个系统并确保您的代码可以从数据库中获取正确的数据的地方。对于集成测试,每个开发人员都应该在他们的工作站上有一个数据库的副本,以便对其进行测试。您应该尝试自动创建和填充数据库,以便可以快速轻松地返回到数据库的良好副本。工具,如 nant DBFit 将帮助您编写数据库创建脚本。

    我不会使用中央数据库进行测试,因为其他开发人员可能会同时对它进行测试,而且它可能不是一个好的状态,您可能会得到误报,并花费大量时间试图调试一个不是问题的问题。

        2
  •  2
  •   Raj More    14 年前

    我更喜欢使用测试数据库而不是 不提交 想法。

    我的集成测试数据库是实际生产数据库的一个副本(这个版本是在我实时推出更改之前用于测试的版本)。

        3
  •  1
  •   Yauheni Sivukha    14 年前

    为了做到这一点,我通常清理数据库,用测试数据填充数据库并用DAL方法获取它。

           [SetUp]
        public void SetUp()
        {
            Database.Clear();
            manager = new ServiceManager();
        }
    
        [TearDown]
        public void TearDown()
        {
            manager.Dispose();
        }
    
        [Test]
        public void InsertAndLoadOrderContract()
        {
            MinOrderModel model = new OrderBuilder().InsertMinimalOrder(manager);
    
            Contract contract = TestObjectGenerator.GenerateEntity<Contract>();
            manager.Contract.InsertOrderContract(model.Order.OrderCompositeId, contract);
    
            Contract selectedContract = manager.Contract.SelectById(contract.ContractId);
    
            AssertContract(selectedContract, contract);
        }
    
        private static void AssertContract(IContract actual, IContract expected)
        {
            Assert.That(actual.AgencyCodeOther, Is.EqualTo(expected.AgencyCodeOther));
            Assert.That(actual.AgencyFK, Is.EqualTo(expected.AgencyFK));
            Assert.That(actual.ContractId, Is.EqualTo(expected.ContractId));
            Assert.That(actual.Ident, Is.EqualTo(expected.Ident));
        }
    

    此测试的某些部分可以替换为更方便的部分:

    1. 用数据建立数据库
    2. 不清理数据库,但滚动
    3. 使用更方便的数据库引擎
        4
  •  0
  •   Igor Zevaka    14 年前

    我会模仿数据库。在测试中处理数据库是痛苦的,因为您需要创建数据库,创建模式,然后删除它,确保没有连接挂起,等等,这是痛苦的。

    下面是一些快速的sql抽象类和示例用法+单元测试。

    public class SqlConnectionBase : IDisposable {
        private readonly SqlConnection m_Connection;
    
        public SqlConnectionBase(string connString) {
            m_Connection = new SqlConnection(connString);
        }
    
        public virtual SqlConnection Object { get { return m_Connection; } }
    
        public virtual void Open() {
            m_Connection.Open();
        }
        public virtual void Close() {
            m_Connection.Close();
        }
    
        #region IDisposable Members
    
        public virtual void Dispose() {
            m_Connection.Dispose();
        }
    
        #endregion
    }
    
    public class SqlCommandBase  : IDisposable{
        private readonly SqlCommand m_Command;
    
        public SqlCommandBase() {
            m_Command = new SqlCommand();
        }
        public SqlCommandBase(string cmdText, SqlConnectionBase connection) {
            m_Command = new SqlCommand(cmdText, connection.Object);
        }
        public SqlCommandBase(SqlConnectionBase connection) {
            m_Command = new SqlCommand();
            m_Command.Connection = connection.Object;
        }
    
        public virtual int ExecuteNonQuery() { return m_Command.ExecuteNonQuery(); }
        public virtual string CommandText { get { return m_Command.CommandText; } set { m_Command.CommandText = value; } }
    
        public virtual void AddParameter(SqlParameter sqlParameter) {
            m_Command.Parameters.Add(sqlParameter);
        }
    
        #region IDisposable Members
    
        virtual public void Dispose() {
            m_Command.Dispose();
        }
    
        #endregion
    }
    public class SqlFactory {
        public virtual SqlCommandBase CreateCommand(string query, SqlConnectionBase conn) {
            return new SqlCommandBase(query, conn);
        }
        public virtual SqlCommandBase CreateCommand(SqlConnectionBase conn) {
            return new SqlCommandBase(conn);
        }
    
        public virtual SqlConnectionBase CreateConnection(string connString) {
            return new SqlConnectionBase(connString);
        }
    
    }
    
    public class DBUser {
       public DBUser(SqlFactory factory) {
         m_factory = factory; //dependency constructor, will be used during unit testing
       }
    
       public DBUser() {
         m_factory = new SqlFactory(); //used during normal execution
       }
    
       public void DoSomething() {
         var conn = m_factory.CreateConnection("server=servername,database=...");
         var cmd =  m_factory.CreateCommand(conn);
         cmd.CommandText = "Select * from users";
         cmd.ExecuteNonQuery();
       }
    
       [TestMethod]
       public void DoSomethingTest() {
         var factoryMock = new Mock<SqlFactory>();
         var cmdMock = new Mock<CommandBase>();
         factoryMock.Setup(f=>f.CreateConnection(It.IsAny<string>())).Returns(cmdMock.Object);
         DBUser dbUser = new DBUser(factoryMock.Object);
         dbUser.DoSomething();
    
         //Verify that DoSomething is called.
         cmdMock.Verify(c=>c.DoSomething());
       }
    }