如何将样本(虚拟)数据添加到单元测试中?

时间:2009-07-08 11:37:35

标签: c# unit-testing testing nunit

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

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

    我区分主数据样本数据,其中前者在系统投入生产时可用(首次安装),后者是典型用途我要求我的测试运行(以及在开发过程中玩)的情况。

    我将所有这些存储在一个Excel文件中(因为它很容易维护),其中每个工作表包含一个特定的实体(例如用户,客户等),并标记为主文件或样本。

    我有2个测试用例,我(想念)用它来导入必要的数据:

    1. InitForDevelopment(创建架构,导入主数据,导入样本数据)
    2. InitForProduction(创建架构,导入主数据)

4 个答案:

答案 0 :(得分:13)

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

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

我不知道这是一个很好的网络参考,但我从Apress出版的Steven Sanderson的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,了解实时项目的控制方法反转,以允许生产代码通过依赖注入自动实例化您的实时存储库。这应该让你更接近你需要的地方。

答案 1 :(得分:1)

在我们公司,我们从几周和一个月开始讨论这些问题。

遵循单元测试指南:

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

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

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

我会说你使用excel文件的方式很好。

我的想法让它变得更好:

  • 如果您的软件背后有数据库谷歌“NDBUnit”。它是一个在数据库中插入和删除数据以进行单元测试的框架。
  • 如果你没有数据库,那么XML在像excel这样的系统上会更灵活。

答案 2 :(得分:1)

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

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

我仍然确定你可能在很多实例中需要虚拟数据,所以如果你已经在使用或者知道模拟框架,那就道歉了。

答案 3 :(得分:1)

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

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

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

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