如何使用相同的数据运行多个单元测试

时间:2019-01-02 00:49:36

标签: asp.net-core xunit

我正在尝试为我创建的rest api进行单元测试。当我分别运行测试时,它们会通过。但是当我尝试一次全部运行它们时,只有1次通过。

我知道这与我尝试在evry测试中使用相同数据有关。我通过为每个测试使用新的数据库(名称)找到了解决方案。但是我想知道这是否是正确的方法吗?因为我认为必须有一种更清洁的方法。我研究了IDisposable方法,但无法使其正常工作。

这是我将数据放入db中进行测试的方式:

public class GameTestShould
{
    private static GameService gameService;

    private readonly DbContextOptions<GameContext> options;
    public readonly Game testGame;
    public readonly User testUser;
    public Location testLocation;


    public GameTestShould()
    {
        //Arrange For the all tests
        options = new DbContextOptionsBuilder<GameContext>().UseInMemoryDatabase(databaseName: "GameTestDB").Options;
        //arrange
        using (var globalContext = new GameContext(options))
        {
            gameService = new GameService(globalContext);

            var testUser = new User()
            {
                Username = "tempUser",
                Password = "123456"
            };
            globalContext.Users.Add(testUser);
            var location2 = new Location()
            {
                LocName = "Standbeeld Stadhuis",
                LocLat = 51.220884,
                LocLong = 4.398995,
                LocDescription = "Standbeeld Vrijheid blijheid nabij stadhuis."
            };
            var location3 = new Location()
            {
                LocName = "Het Steen",
                LocLat = 51.222773,
                LocLong = 4.397367,
                LocDescription = "Het Steen"
            };
            var location4 = new Location()
            {
                LocName = "Pieter Paul Rubens",
                LocLat = 51.219326,
                LocLong = 4.401576,
                LocDescription = "Groenplaats, standbeeld Pieter Paul Rubens."
            };
            var location5 = new Location()
            {
                LocName = "Politiekantoor",
                LocLat = 51.230754,
                LocLong = 4.4174065,
                LocDescription = "Politiekantoor"
            };
            globalContext.Add(location2);
            globalContext.Add(location3);
            globalContext.Add(location4);
            globalContext.Add(location5);

            var suspect0 = new Suspect()
            {
                //  SuspectId = 1,
                SusName = "Miss Scarlett",
                SusWeapon = "Rope",
                SusDescription = "Ms. Vivienne Sakura Scarlet",
                SusImgUrl = "https://i.pinimg.com/originals/95/ce/3d/95ce3da06af8b1c09a4b2d4fa603b7a0.jpg",
            };
            var suspect1 = new Suspect()
            {
                SusName = "Mr. Green",
                SusWeapon = "Wooden cross",
                SusDescription = "Rev. Jonathan Green.",
                SusImgUrl = "https://pbs.twimg.com/profile_images/447953368271814657/Inf33QvJ.jpeg",
            };
            var suspect2 = new Suspect()
            {
                SusName = "Colonel Mustard",
                SusWeapon = "Gun",
                SusDescription = "Col. Michael Mustard",
                SusImgUrl = "https://static.independent.co.uk/s3fs-public/thumbnails/image/2016/07/04/08/unspecified-3.jpg?width=1368&height=912&fit=bounds&format=pjpg&auto=webp&quality=70",
            };

            var suspect3 = new Suspect()
            {
                SusName = "Dr.Orchid",
                SusWeapon = "Syringe",
                SusDescription = "A Doctor, Elegant ",
                SusImgUrl = "https://static.independent.co.uk/s3fs-public/thumbnails/image/2016/07/04/08/unspecified-4.jpg?width=1368&height=912&fit=bounds&format=pjpg&auto=webp&quality=70",
            };
            globalContext.Suspects.Add(suspect0);
            globalContext.Suspects.Add(suspect1);
            globalContext.Suspects.Add(suspect2);
            globalContext.Suspects.Add(suspect3);

            var clue0 = new Clue()
            {
                ClueName = "RansomPuzzle"
            };
            var clue1 = new Clue()
            {
                ClueName = "ARKnife"
            };
            var clue2 = new Clue()
            {
                ClueName = "ARRope"
            };
            globalContext.Clues.Add(clue0);
            globalContext.Clues.Add(clue1);
            globalContext.Clues.Add(clue2);
            globalContext.SaveChanges();
        }
    }

这是第一次测试

   [Fact]
    public void Throw_AppExceptionTooManyItems_Over8ItemsAdded()
    {
        var options = new DbContextOptionsBuilder<GameContext>().UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()).Options; 

        using (var globalContext = new GameContext(options))
        {
            gameService = new GameService(globalContext);
            //act and assert 
            Exception ex = Assert.Throws<AppException>(() => gameService.CreateGame(1, 2));
            Assert.Contains("between", ex.Message);               
        }          
     }

第二次测试。在这里(我认为),我做了另一个数据库,但这不是只留下来自先前测试的数据吗?因此,如果我要进行大量测试,会不会减慢测试过程?

   [Fact]
    public void Throw_AppExceptionWrongUser()
    {
        var options = new DbContextOptionsBuilder<GameContext>().UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()).Options;

        using (var globalContext = new GameContext(options))
        {
            gameService = new GameService(globalContext);
            //act and assert 
            Exception ex = Assert.Throws<AppException>(() => gameService.CreateGame(2, 3));
            Assert.Equal("User does not exist.", ex.Message);
        }            
    }
}

}

还有更好的方法吗?

1 个答案:

答案 0 :(得分:0)

您需要为每个测试提供一个新的数据库,否则,如您所见,该数据库将被重新用于每个测试,并且会影响结果。

您可以阅读the docs,其中每个测试都使用新的InMemoryDatabase。我给数据库起了与测试方法相同的名称(为清楚起见)。

您可以将数据库代码移至私有方法,然后在每个测试中重复使用它,例如。

[Fact]
public void Throw_AppExceptionWrongUser()
{
    var options = new DbContextOptionsBuilder<GameContext>().UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()).Options;

        using (var globalContext = CreateNewContext(options))
        {
            gameService = new GameService(globalContext);
            //act and assert 
            Exception ex = Assert.Throws<AppException>(() => gameService.CreateGame(2, 3));
            Assert.Equal("User does not exist.", ex.Message);
        }            
    }
}

private GameContext CreateNewContext(DbContextOptions options)
{
    var globalContext = new GameContext(options);

    var testUser = new User()
        {
            Username = "tempUser",
            Password = "123456"
        };
    globalContext.Users.Add(testUser);

    return globalContext;
}

性能不应受到太大影响,除非您用大量数据填充内存数据库(但根据示例代码来看,这似乎没有问题)。一种发现的方法是编写测试并对其进行测量:-)

如果性能是一个问题,那么您应该考虑仅使用特定测试所需的数据填充数据库。如果您想沿着这条路线走,我建议您查看Builder pattern来填充数据库。