【问题标题】:How do you add sample (dummy) data to your unit tests?如何将样本(虚拟)数据添加到单元测试中?
【发布时间】:2010-11-09 00:15:29
【问题描述】:

在较大的项目中,我的单元测试通常需要一些“虚拟”(样本)数据才能运行。一些默认客户、用户等。我想知道您的设置是什么样的。

  1. 您如何组织/维护这些数据?
  2. 如何将其应用于单元测试(任何自动化工具)?
  3. 您真的需要测试数据还是您认为它没用?

我目前的解决方案:

我区分主数据样本数据,前者在系统投入生产(首次安装)时可用,后者是典型用途我的测试运行(并在开发期间播放)所需的案例。

我将所有这些都存储在一个 Excel 文件中(因为它非常易于维护),其中每个工作表都包含一个特定的实体(例如用户、客户等),并被标记为 master 或 sample。

我有 2 个测试用例(错过)用于导入必要的数据:

  1. InitForDevelopment(创建架构、导入主数据、导入示例数据)
  2. InitForProduction(创建架构,导入主数据)

【问题讨论】:

    标签: c# unit-testing testing nunit


    【解决方案1】:

    我使用存储库模式并有一个由相关单元测试实例化的虚拟存储库,它提供一组已知的数据,其中包含各个字段范围内和范围外的示例。

    这意味着我可以通过在运行时从测试单元提供实例化存储库进行测试或提供生产存储库(通过依赖注入 (Castle))来测试我的代码。

    我不知道有什么好的网络参考资料,但我从 Steven Sanderson 的 Apress 出版的 Professional ASP.NET MVC 1.0 书籍中学到了很多。 MVC 方法自然地提供了关注点分离,这是允许您的测试以更少的依赖项运行所必需的。

    基本元素是您的存储库实现了一个用于数据访问的接口,然后由您在测试项目中构建的假存储库实现相同的接口。

    在我当前的项目中,我有一个界面:

    namespace myProject.Abstract
    {
        public interface ISeriesRepository
        {
            IQueryable<Series> Series { get; }
        }
    }
    

    这既是作为我的实时数据存储库(使用 Linq to SQL)实现的,也是一个假存储库,因此:

    namespace myProject.Tests.Respository
    {
        class FakeRepository : ISeriesRepository
        {
            private static IQueryable<Series> fakeSeries = new List<Series> {
                new Series { id = 1, name = "Series1", openingDate = new DateTime(2001,1,1) },
                new Series { id = 2, name = "Series2", openingDate = new DateTime(2002,1,30),
                ...
                new Series { id = 10, name = "Series10", openingDate = new DateTime(2001,5,5)
            }.AsQueryable();
    
            public IQueryable<Series> Series
            {
                get { return fakeSeries; }
            }
        }
    }
    

    然后实例化使用数据的类,将存储库引用传递给构造函数:

    namespace myProject
    {
        public class SeriesProcessor
        {
            private ISeriesRepository seriesRepository;
    
            public void SeriesProcessor(ISeriesRepository seriesRepository)
            {
                this.seriesRepository = seriesRepository;
            }
    
            public IQueryable<Series> GetCurrentSeries()
            {
                return from s in seriesRepository.Series
                       where s.openingDate.Date <= DateTime.Now.Date
                       select s;
            }
        }
    }
    

    然后在我的测试中我可以这样处理它:

    namespace myProject.Tests
    {
        [TestClass]
        public class SeriesTests
        {
            [TestMethod]
            public void Meaningful_Test_Name()
            {
                // Arrange
                SeriesProcessor processor = new SeriesProcessor(new FakeRepository());
    
                // Act
                IQueryable<Series> currentSeries = processor.GetCurrentSeries();
    
                // Assert
                Assert.AreEqual(currentSeries.Count(), 10);
            }
    
        }
    }
    

    然后查看 CastleWindsor 以了解您的实时项目的控制反转方法,以允许您的生产代码通过依赖注入自动实例化您的实时存储库。这应该会让你更接近你需要去的地方。

    【讨论】:

    • 听起来是一种有趣的方法,但不知何故我并不完全理解它。对于这种方法,您在网上有任何参考资料吗?
    【解决方案2】:

    在我们公司,我们从几周到几个月的时间里都在讨论这些问题。

    遵循单元测试的指导方针:

    每个测试必须是原子的并且不允许相互关联(没有数据共享),这意味着每个测试必须在开始时有自己的数据并在结束时清除数据。

    Out 产品非常复杂(5 年开发,数据库中有 100 多个表),几乎不可能以可接受的方式维护它。

    我们尝试了数据库脚本,它在测试之前/之后创建和删除数据(有自动方法调用它)。

    我会说你在 excel 文件方面做得很好。

    我的想法让它变得更好:

    • 如果您的软件背后有一个数据库,请搜索“NDBUnit”。这是一个在数据库中插入和删除数据以进行单元测试的框架。
    • 如果您没有数据库,XML 在 excel 等系统上可能会更灵活一些。

    【讨论】:

      【解决方案3】:

      不直接回答问题,但限制需要使用虚拟数据的测试数量的一种方法是使用模拟框架创建模拟对象,您可以使用这些对象来伪造类中任何依赖项的行为。

      我发现使用模拟对象而不是特定的具体实现可以大大减少您需要使用的真实数据量,因为模拟不会处理您传递给它们的数据。它们完全按照您的要求执行。

      我仍然确定您可能在很多情况下都需要虚拟数据,所以如果您已经在使用或知道模拟框架,我们深表歉意。

      【讨论】:

        【解决方案4】:

        为了清楚起见,您需要区分 UNIT 测试(测试一个不隐含依赖于其他模块的模块)和应用程序测试(测试应用程序的部分)。

        对于前者,您需要一个模拟框架(我只熟悉 Perl 框架,但我确信它们存在于 Java/C# 中)。良好框架的标志是能够获取正在运行的应用程序,记录所有方法调用/返回,然后使用记录的数据模拟选定的方法(例如,您未在此特定单元测试中测试的方法)。 对于良好的单元测试,您必须模拟每个外部依赖项 - 例如,不调用文件系统,不调用数据库或其他数据访问层,除非您正在测试,等等...

        对于后者,相同的模拟框架很有用,此外还可以创建测试数据集(可以为每个测试重置)。要为测试加载的数据可以驻留在您可以从中加载的任何离线存储中 - Sybase DB 数据、XML 的 BCP 文件,无论您喜欢什么。我们同时使用 BCP 和 XML。

        请注意,如果您的整个公司框架允许(或者更确切地说是强制执行)“此表别名的真实数据库表名称是什么”API,那么这种“将测试数据加载到数据库”测试会显着更容易。这样一来,您可以让您的应用程序在测试期间查看克隆的“测试”数据库表而不是真实的 - 除了此类表别名 API 的主要目的是使您能够将数据库表从一个数据库移动到另一个数据库。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2018-06-03
          • 2019-09-13
          • 1970-01-01
          • 1970-01-01
          • 2015-03-21
          • 1970-01-01
          • 2017-05-29
          相关资源
          最近更新 更多