Base solution for your next web application
Open Closed

Custom repository dependency injection not working in test project #11599


User avatar
0
XugoWebTeam created

Hi,

I've recently been attempting to get the test project up and running for our project. It appears as though a previous colleague did attempt to setup the mocking of the database using an in-memory SQLite database using a custom DB context which appears to follow the steps outlined here https://aspnetboilerplate.com/Pages/Documents/Articles/Unit-Testing-with-Entity-Framework,-xUnit-Effort/index.html. Following on from that article, I've attempted to write some test cases for another part of our project and noticed that the linked article should support resolving the repository within the test class. However, when I attempt to resolve like this _siteJoinReportRepository = Resolve<ISiteJoinReportRepository>();, I receive an object disposed exception with the following System.ObjectDisposedException Cannot access a disposed context instance. A common cause of this error is disposing a context instance that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling 'Dispose' on the context instance, or wrapping it in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances. . I've tried to update the test base to something like this, which I thought by removing the using, would stop the context from being disposed but the issue still persists.

        private void SeedTestData()
        {
            void NormalizeDbContext(XugoDbContext context)
            {
                context.EntityChangeEventHelper = NullEntityChangeEventHelper.Instance;
                context.EventBus = NullEventBus.Instance;
                context.SuppressAutoSetTenantId = true;
            }

            //Seed initial data for default tenant
            AbpSession.TenantId = 1;

            UsingDbContext(context =>
            {
                NormalizeDbContext(context);
                new TestDataBuilder(context, 1).Create();
                
                var repo = LocalIocManager.Resolve<EventReportRepository>();
                var t = repo.GetTable();
                
            });
        }

        protected IDisposable UsingTenantId(int? tenantId)
        {
            var previousTenantId = AbpSession.TenantId;
            AbpSession.TenantId = tenantId;
            return new DisposeAction(() => AbpSession.TenantId = previousTenantId);
        }

        protected void UsingDbContext(Action<XugoDbContext> action)
        {
            UsingDbContext(AbpSession.TenantId, action);
        }

Please let me know if you need any further information or details.

Thanks.


8 Answer(s)
  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi @XugoWebTeam

    Is it possible for us to test this on your project ? Or is it possible to provide a minimal reproduction project ?

  • User Avatar
    0
    XugoWebTeam created

    Hi, thanks for the quick response. I've downloaded v11.3.0 of the ASP.NET CORE & Angular project and I was able to replicate the issue by simply adding this test class to the base project

    using System.Linq;
    using System.Threading.Tasks;
    using Microsoft.EntityFrameworkCore;
    using Shouldly;
    using Xugo.Authorization.Users;
    using Xunit;
    
    namespace Xugo.Tests;
    
    public class UserManagerTests : AppTestBase
    {
        private IUserRepository _userRepository;
    
        public UserManagerTests()
        {
            _userRepository = Resolve<IUserRepository>();
        }
    
        [Fact]
        public async Task Should_Get_Active_Users()
        {
            // Arrange
            var users = await _userRepository.InsertAsync(new User()
            {
                UserName = "test", IsActive = true, TenantId = 1, Name = "john", Surname = "smith",
                EmailAddress = "[email protected]", Password = "test", NormalizedUserName = "john",
                NormalizedEmailAddress = "[email protected]"
            });
    
            // Act
            var activeUsers = _userRepository.GetAll()
                .Where(u => u.IsActive)
                .Select(u => u.Id)
                .ToListAsync();
    
            // Assert
            await activeUsers.ShouldNotBeNull();
        }
    }
    

    This line doesn't appear to render correctly in the code block: _userRepository = Resolve<IUserRepository>();

  • User Avatar
    0
    XugoWebTeam created

    Hi @ismcagdas, was this issue reproducible using the above?

  • User Avatar
    0
    m.aliozkaya created
    Support Team

    Hi @XugoWebTeam,

    I think this problem is related to the unit of work. You can use the following code for testing

    await UsingDbContextAsync(async context =>
    {
        var activeUsers = await context.Users
            .Where(u => u.IsActive)
            .Select(u => u.Id)
            .ToListAsync();
    
        activeUsers.ShouldNotBeNull();
    });
    
  • User Avatar
    0
    XugoWebTeam created

    Unfortunately, I need to provide the repository as it's an argument of the static method I'm trying to test, it looks as though we should be able to resolve the repository as in the example here https://aspnetboilerplate.com/Pages/Documents/Articles/Unit-Testing-with-Entity-Framework,-xUnit-Effort/index.html#ArticleUsingRepositories . Could it be something to do with how it is registered in for the IocManager here?

    public static void Register(IIocManager iocManager)
    {
        RegisterIdentity(iocManager);
    
        var builder = new DbContextOptionsBuilder<XugoDbContext>();
    
        var inMemorySqlite = new SqliteConnection("Data Source=:memory:");
        builder.UseSqlite(inMemorySqlite);
    
        iocManager.IocContainer.Register(
            Component
                .For<DbContextOptions<XugoDbContext>>()
                .Instance(builder.Options)
                .LifestyleSingleton()
        );
    
        inMemorySqlite.Open();
    
        new XugoDbContext(builder.Options).Database.EnsureCreated();
    }
    

    Otherwise, how do you think the unit of work may be causing the issue?

  • User Avatar
    0
    m.aliozkaya created
    Support Team

    Hi @XugoWebTeam,

    The following snippet may help solve your problem. If the problem persists, let us know.

    public class UserManagerTests : AppTestBase
    {
        private readonly IUserRepository _userRepository;
        private readonly IUnitOfWorkManager _unitOfWorkManager;
        
        public UserManagerTests()
        {
            _userRepository = Resolve<IUserRepository>();
            _unitOfWorkManager = Resolve<IUnitOfWorkManager>();
        }
        
        [Fact]
        public async Task Should_Get_Active_Users()
        {
            // Arrange
            var users = await _userRepository.InsertAsync(new User()
            {
                UserName = "test", IsActive = true, TenantId = 1, Name = "john", Surname = "smith",
                EmailAddress = "[email protected]", Password = "test", NormalizedUserName = "john",
                NormalizedEmailAddress = "[email protected]"
            });
            
            using var uow = _unitOfWorkManager.Begin();
            var activeUsers = await _userRepository.GetAll()
                .Where(u => u.IsActive)
                .Select(u => u.Id)
                .ToListAsync();
                
            activeUsers.ShouldNotBeNull();
            
            // var activeUsers = _userRepository.GetAll()
            //     .Where(u => u.IsActive)
            //     .Select(u => u.Id)
            //     .ToListAsync();
            //
            // await activeUsers.ShouldNotBeNull();
        }
    }
    
  • User Avatar
    0
    XugoWebTeam created

    Yes, thank you, that appears to fix the issue. However, because I have a custom repository called like this within the method that I'm trying to test

    var sitesdb = await siteJoinRepository.GetAllIncluding(c => c.Site, t => t.Site.SiteType).Where(t => t.XugoEventId == eventId).ToListAsync();
    

    I imagine that I will have to wrap any method call which uses is a repository in a unit of work. Is there another way to handle this so that I don't need to do this?

  • User Avatar
    0
    m.aliozkaya created
    Support Team

    Hi @XugoWebTeam,

    Could you check this article? I think this attribute can solve your problem https://aspnetboilerplate.com/Pages/Documents/Unit-Of-Work#unitofwork-attribute