Inject Entity Framework Core Context into repository with ServiceStack when unit testing

entity-framework-core servicestack unit-testing

Question

I have a repository class

public class PersonRepository : IPersonRepository
{
    private DataContext _context;

    public PersonRepository(DataContext context)
    {
        _context = context;
    }

    public List<PersonDto> Fetch() ......
}

I then have a ServiceStack PersonsService

public class PersonsServices : Service
{
    private IPersonsRepository _personRepo;

    public PersonsServices(IPersonsRepository personRepository)
    {
        _personRepo = personRepository;
    }

    public object Any(GetPersons request)
    {
        return new GetPersonssResponse
        {
            Results = _personsRepo.Fetch()
        };
    }
}

My code works fine in the ServiceStack app as the DataContext is injected in by .Net Core as configured in the AddDbContext method in Startup.cs

services.AddDbContext<DataContext>(x => x
    .UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

How do I do this in a unit test in conjunction with ServiceStack and using .Net Core Entity Framework?

I need something equivalent to the AddDbContext here. Ultimately my goal is to create unit tests that use an in-memory or SQLite context but keep the same repository code.

EDIT: Here is what my unit test looks like so far.

[TestFixture]
    public class PersonTest
    {
        private ServiceStackHost appHost;

        [SetUp]
        public void TestFixtureSetUp()
        {
            appHost = new BasicAppHost().Init();
            var container = appHost.Container;

            IConfigurationRoot configuration = new ConfigurationBuilder()
                   .SetBasePath(Directory.GetCurrentDirectory())
                   .AddJsonFile("appsettings.json")
                   .Build();

            var optionsBuilder = new DbContextOptionsBuilder<DataContext>();
            optionsBuilder
                .UseSqlite(configuration.GetConnectionString("DefaultConnection"));

//HOW TO GET SERVICESTACK / FUNQ TO RESOLVE THE DataContext IN REPOSITORY???

            **container.Register<IDataContext>(i => new DataContext(optionsBuilder.Options)).ReusedWithin(ReuseScope.Request);**
            container.RegisterAutoWiredAs<PersonRepository, IPersonRepository>();

        }

        [Test]
        public async Task GetPersons()
        {
            var service = appHost.Container.Resolve<PersonsServices>();

            var response = await service.Any(new GetPersons { });
            var results = (GetPersonsResponse)response;

            Assert.That(1 == 1);
        }

        [TearDown]
        public void TestFixtureTearDown()
        {
            appHost.Dispose();
        }
    }
1
1
12/10/2018 1:39:15 PM

Popular Answer

This documentation on Unit Testing .NET Core EF shows you how can create an In Memory DataContext which you can then inject into your Services constructor:

var options = new DbContextOptionsBuilder<BloggingContext>()
    .UseInMemoryDatabase(databaseName: "Add_writes_to_database")
    .Options;

// Run the test against one instance of the context
using (var context = new BloggingContext(options))
{
    var service = new BlogService(context);
}

So you could pass in the DataContext and Repository in your Service:

using (var service = new PersonsServices(new PersonRepository(new DataContext(...))))
{
    var response = service.Any(new GetPersons { });
}

Or register the EF DataContext in Funq's IOC:

container.Register<DataContext>(i => new DataContext(optionsBuilder.Options));
container.RegisterAutoWiredAs<PersonRepository, IPersonRepository>();

//...
using (var service = appHost.Container.Resolve<PersonsServices>())
{
}

Tips

  • What you register needs to be the exact type that's being resolved, e.g. since PersonRepository accepts a DataContext you must register a DataContext not IDataContext.
  • Don't use Request Scope outside of a HTTP Request, you can use either transient or singleton since there's only a single thread in a unit test.
  • If you're going to call your Services methods directly you may as well specify the Type on your Service method.

E.g:

public GetPersonsResponse Any(GetPersons request) => 
    new GetPersonsResponse {
        Results = _personsRepo.Fetch()
    };

Which will avoid having to cast to the response in your Unit test (which is wrong in the example provided as it's trying to await a synchronous method returning an object). Specifying the Response Type or object has no effect in ServiceStack

If you're using an In Memory Database consider creating Integration Tests instead where you can use AppSelfHostBase which for .NET Core is in the ServiceStack.Kestrel package. You can override ConfigureServices() to register dependencies in .NET Core's IOC.

1
12/10/2018 2:09:07 PM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow