【问题标题】:What is the best strategy for setting up test data in integration tests?在集成测试中设置测试数据的最佳策略是什么?
【发布时间】:2017-12-18 13:03:59
【问题描述】:

我正在尝试为我的存储库实施集成测试,但在每次集成测试之前选择正确的插入测试数据的策略时遇到了困难。

这就是我的存储库测试类的样子;

  [TestFixture]
  public class RealtyTest : ITest<IRealtyRepository>
  {
    public IUnitOfWork Uow { get; set; }

    public IRealtyRepository Repository { get; set; }

    public ITestEnvironment TestEnvironment { get; set; }

    [OneTimeSetUp]
    public void OneTimeSetup()
    {
        TestEnvironment = new RealtyTestEnvironment();
        TestEnvironment.Prepare();
    }

    [SetUp]
    public void Setup()
    {
        Uow = AppCore.Instance.RealtyUow;
        Uow.BeginTransaction();
        Repository = ((IRealtyUow)Uow).RealtyRepository;
    }

    [Test]
    public void should_get_realty_detail_by_id()
    {
        Realty realty = Repository.GetDetail(1);

        Assert.IsNotNull(realty.Firm);
        Assert.IsNotNull(realty.FirmUser);
        Assert.IsNotNull(realty.Category);
        Assert.IsNotNull(realty.SubCategory);
        Assert.IsNotNull(realty.Publish);
        Assert.IsNotNull(realty.ResIdence);
        Assert.IsNotNull(realty.Star);
        Assert.IsNotNull(realty.Floor);
        Assert.IsNotNull(realty.Heating);
        Assert.IsNotNull(realty.Fuel);
        Assert.IsNotNull(realty.BuildState);
        Assert.IsNotNull(realty.Usage);
        Assert.IsNotNull(realty.Credit);
        Assert.IsNotNull(realty.Register);
        Assert.IsNotNull(realty.Activate);
        Assert.IsNotNull(realty.District);
        Assert.IsNotNull(realty.District.County);
        Assert.IsNotNull(realty.District.County.City);
        Assert.IsNotNull(realty.District.County.City.Country);
    }

    [Test]
    public void should_get_realty_detail_all_by_id()
    {
       Realty realty = Repository.GetWithChilds(1);

        Assert.IsNotNull(realty.Firm);
        Assert.IsNotNull(realty.FirmUser);
        Assert.IsNotNull(realty.Category);
        Assert.IsNotNull(realty.SubCategory);
        Assert.IsNotNull(realty.Publish);
        Assert.IsNotNull(realty.ResIdence);
        Assert.IsNotNull(realty.Star);
        Assert.IsNotNull(realty.Floor);
        Assert.IsNotNull(realty.Heating);
        Assert.IsNotNull(realty.Fuel);
        Assert.IsNotNull(realty.BuildState);
        Assert.IsNotNull(realty.Usage);
        Assert.IsNotNull(realty.Credit);
        Assert.IsNotNull(realty.Register);
        Assert.IsNotNull(realty.Activate);
        Assert.IsNotNull(realty.District);
        Assert.IsNotNull(realty.District.County);
        Assert.IsNotNull(realty.District.County.City);
        Assert.IsNotNull(realty.District.County.City.Country);

        Assert.Greater(realty.Files.Count, 0);
        Assert.Greater(realty.Attributes.Count, 0);
    }

    [TearDown]
    public void TearDown()
    {
        if (Uow != null)
        {
            Uow.Rollback();
            Uow.Dispose();
        }
    }

    [OneTimeTearDown]
    public void OneTimeTearDown()
    {
        TestEnvironment.Rollback();
    }
}

正如您在 OneTimeSetup() 方法中看到的那样,在测试类中完成每个集成测试方法后,我正在插入测试数据并在 OneTimeTearDown() 方法上删除。 对于创建测试数据,我使用了 NDbUnit 库。

这是我的 RealtyTestEnvironment 类实现;

 public class RealtyTestEnvironment : ITestEnvironment
{
    private NDbUnitTest _database = null;

    public int TestRowCount { get; set; }

    public RealtyTestEnvironment()
    {
        TestRowCount = 1;
    }

    public void Prepare()
    {
        _database = new SqlDbUnitTest(TestSettings.HemlakTestDbConnection);
        _database.ReadXmlSchema(string.Format(@"{0}\HemlakDb\Hemlak.xsd", TestSettings.AppRootPath));
        _database.PerformDbOperation(DbOperationFlag.DeleteAll);

        GeneralTestDataBuilder generalDataBuilder = new GeneralTestDataBuilder();
        _database.ReadXml(generalDataBuilder.GetTestTypeStream(TestRowCount));
        _database.PerformDbOperation(DbOperationFlag.InsertIdentity);

        LocationTestDataBuilder locationDataBuilder = new LocationTestDataBuilder();
        _database.ReadXml(locationDataBuilder.GetTestCountryStream(TestRowCount));
        _database.PerformDbOperation(DbOperationFlag.InsertIdentity);
        _database.ReadXml(locationDataBuilder.GetTestCityStream(TestRowCount));
        _database.PerformDbOperation(DbOperationFlag.InsertIdentity);
        _database.ReadXml(locationDataBuilder.GetTestCountyStream(TestRowCount));
        _database.PerformDbOperation(DbOperationFlag.InsertIdentity);
        _database.ReadXml(locationDataBuilder.GetTestDistrictStream(TestRowCount));
        _database.PerformDbOperation(DbOperationFlag.InsertIdentity);

        FirmTestDataBuilder firmDataBuilder = new FirmTestDataBuilder();
        _database.ReadXml(firmDataBuilder.GetTestFirmStream(TestRowCount));
        _database.PerformDbOperation(DbOperationFlag.InsertIdentity);
        _database.ReadXml(firmDataBuilder.GetTestFirmUserStream(TestRowCount));
        _database.PerformDbOperation(DbOperationFlag.InsertIdentity);

        RealtyTestDataBuilder realtyDataBuilder = new RealtyTestDataBuilder();
        _database.ReadXml(realtyDataBuilder.GetTestRealtyStream(TestRowCount));
        _database.PerformDbOperation(DbOperationFlag.InsertIdentity);
        _database.ReadXml(realtyDataBuilder.GetTestRealtyFileStream(TestRowCount));
        _database.PerformDbOperation(DbOperationFlag.InsertIdentity);
        _database.ReadXml(realtyDataBuilder.GetTestRealtyAttributeStream(TestRowCount));
        _database.PerformDbOperation(DbOperationFlag.InsertIdentity);
    }

    public void Rollback()
    {
        if (_database != null)
        {
            _database.PerformDbOperation(DbOperationFlag.DeleteAll);
        }
    }
}

所以我选择了 NDbUnit,但我觉得不舒服,因为项目的最后提交日期是 3 年前,并且需要花费太多精力来准备测试环境。

有些人使用 Repository 类本身来插入测试数据,所以现在我们使用 Repository 类来插入测试数据来测试那个 Repository 类??这对我来说没有意义。

-你的方法是什么,你如何插入测试数据来测试你的存储库?

-我的实现怎么样,我应该继续使用 Ndbunit 吗?

【问题讨论】:

    标签: repository integration-testing ndbunit


    【解决方案1】:

    有几个选项可以为需要数据存储的逻辑编写集成测试:

    • 模拟数据存储依赖并使用它来代替真实的数据库。经常使用 NSubstitute 或 Moq 之类的东西;
    • 使用另一个更快的数据库作为替代。 EF Core 提供了开箱即用的in-memory database,或者您可以使用Sqlite 之类的东西;
    • 使用真实数据库。

    我个人通常选择后者以避免行为差异和缺乏功能,同时使用内存数据库之类的东西,与真实数据库相比。

    然后你需要初始化/清理你的数据库,这里也有一些选项。

    数据播种可以这样实现:

    • 每个测试都会创建它需要的实体;
    • 重用一段代码,一次性为所有测试创建所有实体;
    • 手工编写的sql脚本;
    • 使用一些库。我知道几个:NDbUnit,这是你提到的。它有点过时了,但或多或​​少有一些新鲜的叉子。我目前正在开发 Reseed,因为我对 NDbUnit 不太满意。

    还有一些事后清理的方法:

    • 为每次测试从头开始创建数据库;
    • 从备份中恢复数据库;
    • 从快照恢复数据库;
    • 将测试包装在事务中,然后将其还原;
    • 在同一测试中手动恢复在每个测试中所做的更改;
    • 使用插入和删除sql脚本将数据恢复到初始状态; 使用库为您插入和删除数据。 Reseed 也可以处理,还有Respawn

    所有这些都可以以某种方式结合起来以实现最佳性能。您可以使用TestContainers 之类的东西将数据库托管在docker 中,并通过按需测试来启动它们,而不是使用专用的Sql Server。

    至于测试框架集成,您选择的方法非常适合该问题。 [OneTimeSetUp][OneTimeTearDown][FixtureSetUp][FixtureTearDown][SetUp][TearDown] 等属性是在运行测试之前/之后执行数据库初始化和清理的好方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-03-15
      • 2017-11-16
      • 2010-09-13
      • 1970-01-01
      • 1970-01-01
      • 2021-04-13
      • 1970-01-01
      相关资源
      最近更新 更多