1

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

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

  [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 吗?

4

1 回答 1

0

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

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

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

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

数据播种可以这样实现:

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

之后还有一些方法可以清理它:

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

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

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

于 2021-11-11T12:34:26.050 回答