单元测试用例的最佳实践

Best practice for unit test cases

我正在使用 xUnit.net 测试框架,并且在每个单元测试中我都有特定的步骤,我在每种情况下都会执行这些步骤。我想知道是否有一种方法可以在我的单元案例开始之前调用此方法一次,并在所有单元测试案例都已执行时调用。

例如:在下面的场景中,我有两个单元案例,在每个案例中,我都在创建一个本地数据库,用数据填充它,然后 运行 我的测试,完成后我调用方法删除数据库。我在每个测试用例中都这样做。我想创建一次并填充一次,然后在执行完所有测试用例后删除数据库,而不是多次创建。删除我创建的内容对我来说很重要,因为测试用例有某些案例,如果在执行测试时未创建数据库,这些案例将会失败。

[Fact]
public void UnitCase1()
{
   CreateDb();
   UploadData();
   ...//My set of operation to test this case
   ...//Assert
   DeleteDb()
}

[Fact]
public void UnitCase2()
{
   CreateDb();
   UploadData();
   ...//My set of operation to test this case
   ...//Assert
   DeleteDb()
}

在埃里克的回答后编辑:(我试过了,但它不起作用)

public class CosmosDataFixture : IDisposable
    {
        public static readonly string CosmosEndpoint = "https://localhost:8081";
        public static readonly string EmulatorKey = "Mykey";
        public static readonly string DatabaseId = "Databasename";
        public static readonly string RecordingCollection = "collectionName";
        string Root = Directory.GetParent( Directory.GetCurrentDirectory() ).Parent.Parent.FullName;
        DocumentClient client = null;

        public void ReadAllData( DocumentClient client )
        {
           //reading document code
        }

        public void ReadConfigAsync()
        {
            client = new DocumentClient( new Uri( CosmosEndpoint ), EmulatorKey,
                 new ConnectionPolicy
                 {
                     ConnectionMode = ConnectionMode.Direct,
                     ConnectionProtocol = Protocol.Tcp

                 } );
        }
 public void CreateDatabase()
        {// create db code
        }
private void DeleteDatabase()
        {
          // delete db code
        }
     public CosmosDataFixture()
        {
            ReadConfigAsync();
            CreateDatabase();
            ReadAllData( client );

        }

        public void Dispose()
        {
            DeleteDatabase();
        }
    }
public class CosmosDataTests : IClassFixture<CosmosDataFixture>
    {
        CosmosDataFixture fixture;

        public CosmosDataTests( CosmosDataFixture fixture )
        {
            this.fixture = fixture;
        }

        [Fact]
        public async Task CheckDatabaseandCollectionCreation()
        {          
            List<string> collectionName = new List<string>();
            var uri = UriFactory.CreateDatabaseUri(DatabaseId);// don't get DatabaseId or client says does not exist in current context
            var collections = await client.ReadDocumentCollectionFeedAsync( uri );
            foreach( var collection in collections )
            {
                collectionName.Add( collection.Id);
            }
                
        }

根据我的测试经验,我在这里看到 2 点: 1-如果您正在检查从数据库到程序中另一点的数据是否正确传输,即集成测试,并且它应该超出单元测试计划的范围,请确保单元测试人员的职责很清楚你在哪里工作,因为有些公司通过假设如果功能测试是 'OK',那么集成也应该是,从而避免集成测试级别。

2- 你在最后提到

It is important for me to delete what I have created as the test cases has certain cases which will fail if Database is not created when the tests are executed

但是

I would like to create once and populate once and then delete db once all test case has been executed.

如果我理解正确,您需要为每个测试用例执行此操作,因为并非所有测试用例都检查相同的场景,所以看起来这些陈述才是真正的问题所在。

回答你的问题,因为你似乎想在下一个版本中以最少的维护来自动化这个过程,而且我也知道工作环境如何倾向于让你做一些不应该做的事情,我可以想到一个前置条件函数和一个后置条件函数,只需执行一次即可。

如果由于某种原因无法做到这一点,请尝试在开始时创建另一个测试用例(如测试用例 0),在其中创建和填充数据库(如果适用,或在需要时将其分开),然后再创建一个你删除它的结尾。

我不熟悉你使用的框架,但我在测试、打开测试关卡和自动化任务方面有很多经验,希望我的回答能对你有所帮助。

这就是 [SetUp][TearDown] 在 NUnit 中的作用。它们分别在每个测试用例之前和之后 运行。在 xUnit 中,您通常会实现默认构造函数和 IDisposable.

例如:


public TestClass()
{
   CreateDb();
   UploadData();
}


public void Dispose()
{
   DeleteDb()
}

[Fact]
public void UnitCase1()
{
   ...//My set of operation to test this case
   ...//Assert
}

[Fact]
public void UnitCase2()
{
   ...//My set of operation to test this case
   ...//Assert
}

正如其他人指出的那样,这种测试在主流说法中不是单元测试,而是集成测试。 xUnit.net 是用于此类测试的良好框架,因此除了语义区别外,它几乎没有技术差异。

除了在测试 class' 构造函数中设置数据库并在 Dispose 中将其拆除外,如 Eric Sc​​haefer 所述,您还可以使用 xUnit.net 的 BeforeAfterTestAttribute。然后,您将覆盖 Before 来设置数据库,并覆盖 After 来拆除它:

public class UseDatabaseAttribute : BeforeAfterTestAttribute
{
    public override void Before(MethodInfo methodUnderTest)
    {
        CreateDb();
        UploadData();

        base.Before(methodUnderTest);
    }

    public override void After(MethodInfo methodUnderTest)
    {
        base.After(methodUnderTest);
        DeleteDb();
    }
}

然后您可以使用该属性注释每个测试方法或整个测试 class。我通常只注释 class:

[UseDatabase]
public class DbTests
{
    // Tests go here...
}

由于使用数据库的测试与共享资源(数据库)进行交互,因此它们无法轻松地 运行 并行进行。默认情况下,xUnit.net 运行s 并行测试,因此您可能希望禁用它。您可以通过添加 xunit.runner.json 文件来实现:

{
  "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json",
  "parallelizeTestCollections": false
}

最后,至少如果您使用的是 SQL 服务器,连接池将阻止您删除数据库。您可以关闭测试的连接池,或者 forcibly close other connections before teardown.