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

发布于 2024-07-26 15:37:23 字数 517 浏览 4 评论 0原文

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

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

我当前的解决方案:

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

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

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

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

In bigger projects my unit tests usually require some "dummy" (sample) data to run with. Some default customers, users, etc. I was wondering how your setup looks like.

  1. How do you organize/maintain this data?
  2. How do you apply it to your unit tests (any automation tool)?
  3. Do you actually require test data or do you think it's useless?

My current solution:

I differentiate between Master data and Sample data where the former will be available when the system goes into production (installed for the first time) and the latter are typical use cases I require for my tests to run (and to play during development).

I store all this in an Excel file (because it's so damn easy to maintain) where each worksheet contains a specific entity (e.g. users, customers, etc.) and is flagged either master or sample.

I have 2 test cases which I (miss)use to import the necessary data:

  1. InitForDevelopment (Create Schema, Import Master data, Import Sample data)
  2. InitForProduction (Create Schema, Import Master data)

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

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

发布评论

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

评论(4

羁绊已千年 2024-08-02 15:37:23

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

这意味着我可以通过从测试单元提供实例化存储库以进行测试或在运行时提供生产存储库(通过依赖项注入 (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 的实时项目的控制反转方法,以允许您的生产代码通过依赖项注入自动实例化实时存储库。 这应该会让你更接近你需要去的地方。

I use the repository pattern and have a dummy repository that's instantiated by the unit tests in question, it provides a known set of data that encompasses a examples that are both within and out of range for various fields.

This means that I can test my code unchanged by supplying the instantiated repository from the test unit for testing or the production repository at runtime (via a dependency injection (Castle)).

I don't know of a good web reference for this but I learnt much from Steven Sanderson's Professional ASP.NET MVC 1.0 book published by Apress. The MVC approach naturally provides the separation of concern that's necessary to allow your testing to operate with fewer dependencies.

The basic elements are that you repository implements an interface for data access, that same interface is then implemented by a fake repository that you construct in your test project.

In my current project I have an interface thus:

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

This is implemented as both my live data repository (using Linq to SQL) and also a fake repository thus:

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; }
        }
    }
}

Then the class that's consuming the data is instantiated passing the repository reference to the constructor:

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;
        }
    }
}

Then in my tests I can approach it thus:

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);
        }

    }
}

Then look at CastleWindsor for the inversion of control approach for your live project to allow your production code to automatically instantiate your live repository through dependency injection. That should get you closer to where you need to be.

无人问我粥可暖 2024-08-02 15:37:23

在我们公司,我们从数周到数月以来多次讨论这些问题。

遵循单元测试的准则:

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

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

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

我想说你对 Excel 文件的处理很好。

我的想法是让它变得更好:

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

In our company we discuss exact these problem a bunch of time since weeks and month.

To follow the guideline of unit testing:

Each test must be atomar and don't allow relate to each other (No data sharing), that means, each tust must be have there own data at the beginning and clear the data at end.

Out product is so complex (5 years development, over 100 tables in a database), that is nearly impossible to maintain this in a acceptable way.

We tried out database scripts, which creates and deletes the data before / after the test (there are automatic methods which call it).

I would say you are on a good way with excel files.

Ideas from me to make it a little well:

  • If you have a database behind your software google for "NDBUnit". It's a framework to insert and delete data in databases for unit tests.
  • If you have no database maybe XML is a little more flexible on systems like excel.
放我走吧 2024-08-02 15:37:23

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

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

我仍然确信您在很多情况下可能需要虚拟数据,因此如果您已经在使用或了解模拟框架,我深表歉意。

Not directly answering the question but one way to limit the amount of tests that need to use dummy data is to use a mocking framework to create mocked objects that you can use to fake the behavior of any dependencies you have in a class.

I find that using mocked objects rather then a specific concrete implementation you can drastically reduce the amount of real data you need to use as mocks don't process the data you pass into them. They just perform exactly as you want them to.

I'm still sure you probably need dummy data in a lot of instances so apologies if you're already using or are aware of mocking frameworks.

抽个烟儿 2024-08-02 15:37:23

需要明确的是,您需要区分 UNIT 测试(测试不隐含依赖于其他模块的模块)和应用程序测试(测试应用程序的部分)。

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

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

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

Just to be clear, you need to differenciate between UNIT testing (test a module with no implied dependencies on other modules) and app testing (test parts of application).

For the former, you need a mocking framework (I'm only familiar with Perl ones, but i'm sure they exist in Java/C#). A sign of a good framework would be ability to take a running app, RECORD all the method calls/returns, and then mock the selected methods (e.g. the ones you are not testing in this specific unit test) using recorded data.
For good unit tests you MUST mock every external dependency - e.g., no calls to filesystem, no calls to DB or other data access layers unless that is what you are testing, etc...

For the latter, the same mocking framework is useful, plus ability to create test data sets (that can be reset for each test). The data to be loaded for the tests can reside in any offline storage that you can load from - BCP files for Sybase DB data, XML, whatever tickles your fancy. We use both BCP and XML.

Please note that this sort of "load test data into DB" testing is SIGNIFICANTLY easier if your overall company framework allows - or rather enforces - a "What is the real DB table name for this table alias" API. That way, you can cause your application to look at cloned "test" DB tables instead of real ones during testing - on top of such table aliasing API's main purpose of enabling one to move DB tables from one database to another.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文