如何在xUnit和nSubstitute中对服务调用进行单元测试

时间:2017-12-09 03:53:57

标签: unit-testing asp.net-core nunit nsubstitute

我一直想弄清楚我是如何进行单元测试服务的,到目前为止还没有。

我正在使用xUnit和NSubstitute(由朋友建议),下面是我想要运行的简单测试(当前失败)。

public class UnitTest1
{
    private readonly RallyService _rallyService;

    public UnitTest1(RallyService rallyService)
    {
        _rallyService= rallyService;
    }

    [Fact]
    public void Test1()
    {
        var result = _rallyService.GetAllRallies();

        Assert.Equal(2, result.Count());
    }
}

我的拉力赛服务类只是简单地调用数据库来获取所有Rally entites并返回它们:

public class RallyService : IRallyService
{
    private readonly RallyDbContext _context;

    public RallyService(RallyDbContext context)
    {
        _context = context;
    }

    public IEnumerable<Rally> GetAllRallies()
    {
        return _context.Rallies;
    }
}

任何指导都将不胜感激。

2 个答案:

答案 0 :(得分:2)

由于您使用的是.NET Core,我假设您还使用了Entity Framework Core。尽管可以模拟先前EF版本中的大多数操作,但EF Core建议使用in-memory database进行单元测试。即您不需要模拟RallyDbContext,因此此特定测试不需要NSubstitute。在使用该服务测试控制器或应用程序时,您需要NSubstitute来模拟服务。

以下是使用内存数据库编写的Test1

public class UnitTest1
{
    private readonly DbContextOptions<RallyDbContext> _options;

    public UnitTest1()
    {
        // Use GUID for in-memory DB names to prevent any possible name conflicts
        _options = new DbContextOptionsBuilder<RallyDbContext>()
            .UseInMemoryDatabase(Guid.NewGuid().ToString())
            .Options;
    }

    [Fact]
    public async Task Test1()
    {
        using (var context = new RallyDbContext(_options))
        {
            //Given 2 records in database
            await context.AddRangeAsync(new Rally { Name = "rally1" }, new Rally { Name = "rally2" });
            await context.SaveChangesAsync();
        }

        using (var context = new RallyDbContext(_options))
        {
            //When retrieve all rally records from the database
            var service = new RallyService(context);
            var rallies = service.GetAllRallies();

            //Then records count should be 2
            Assert.Equal(2, rallies.Count());
        }
    }
}

使用此单元测试的工作测试应用程序在我的GitHub中供您参考。我在实际应用程序中使用了SQL Express。

答案 1 :(得分:0)

我认为使用带参数的单元测试构造函数是标准的。单元测试运行器将新增这个类,除非你使用的东西将自动注入该参数,我认为测试将无法运行。

这是一个标准的夹具布局:

public class SampleFixture {

    [Fact]
    public void SampleShouldWork() {
        // Arrange stuff we need for the test. This may involved configuring
        // some dependencies, and also creating the subject we are testing.
        var realOrSubstitutedDependency = new FakeDependency();
        realOrSubstitutedDependency.WorkingItemCount = 42;
        var subject = new Subject(realOrSubstitutedDependency);

        // Act: perform the operation we are testing
        var result = subject.DoWork();

        // Assert: check the subject's operation worked as expected
        Assert.Equal(42, result);
    }

    [Fact]
    public void AnotherTest() { /* ... */ }
}

如果您需要在测试之间进行通用设置,则可以使用无参数构造函数并在那里进行常规初始化。

就您要测试的特定课程而言,您需要确保您的RallyDbContext处于已知状态,以便可重复且可靠地进行测试。您可能希望查找特定于测试实体框架的答案以获取更多信息。