0

I been trying to figure out how i can unit test service and so far have got nowhere.

I am using xUnit and NSubstitute (as advised by friends), below is the simple test that i want to run (which fails currently).

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());
    }
}

My rally service class makes a simple call to the db to get all Rally entites and returns those:

public class RallyService : IRallyService
{
    private readonly RallyDbContext _context;

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

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

Any guidance would be appreciated.

Aeseir
  • 7,754
  • 10
  • 58
  • 107

2 Answers2

3

Since you use .NET Core, I assume you also use Entity Framework Core. While it was possible to mock most of the operations in the previous EF version, however the EF Core suggests to use in-memory database for unit testing. I.e. you don't need to mock RallyDbContext, hence NSubstitute is not needed for this particular test. You would need NSubstitute to mock the service when testing a controller or application using the service.

Below is your Test1 written using in-memory database.

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());
        }
    }
}

A working test application with this unit test is in my GitHub for your reference. I used SQL Express in the actual app.

Ignas
  • 4,092
  • 17
  • 28
0

I don't think it is standard to have a unit test constructor with a parameter. The unit test runner will new up this class, and unless you are using something that will auto-inject that parameter I think the test will fail to run.

Here is a standard fixture layout:

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() { /* ... */ }
}

If you need a common setup between tests, you can use a parameterless constructor and do common initialisation there.

In terms of the specific class you are trying to test, you need to make sure your RallyDbContext is in a known state to repeatably and reliably test. You may want to look up answers specific to testing Entity Framework for more information.

David Tchepak
  • 9,826
  • 2
  • 56
  • 68