Search code examples
unit-testingabp-framework

ABP Framework login as tenant in application tests


I am using abp 4.4.2 and want to implement some test for one simple crud application service which has no custom code and only inherits from CrudAppService. The entity in this service is an IMultiTenant and I want to check the GetListAsync method with different tenant Ids. I have mocked the ICurrentTenant in the application test base to mock the tenant login like below, however when I use my LoginAsDefaultTenant method, I get a stack overflow error in AutoFac module.

public abstract class OrderManagementApplicationTestBase : OrderManagementTestBase<OrderManagementApplicationTestModule>
{
    private ICurrentTenant _fakeCurrentTenant;
    private ICurrentUser _fakeCurrentUser;

    protected override void AfterAddApplication(IServiceCollection services)
    {
        _fakeCurrentTenant = Substitute.For<ICurrentTenant>();
        _fakeCurrentUser = Substitute.For<ICurrentUser>();
        services.AddSingleton(_fakeCurrentTenant);
        services.AddSingleton(_fakeCurrentUser);
    }

    protected void LoginAsHostAdmin()
    {
        _fakeCurrentTenant.Id.Returns(ci => null);
        _fakeCurrentTenant.Name.Returns(ci => null);
    }

    protected void LoginAsDefaultTenant()
    {
        _fakeCurrentTenant.Id.Returns(ci => Guid.Parse(OrderManagementTestData.DefaultTenantId));
        _fakeCurrentTenant.Name.Returns(ci => TestData.DefaultTenantName);

        _fakeCurrentUser.Id.Returns(ci => TestData.DefaultTenantAdminUserId);
        _fakeCurrentUser.TenantId.Returns(ci => Guid.Parse(OrderManagementTestData.DefaultTenantId));
    }

These unit tests are passing ok:

[Fact]
        public void LoginAsHostAdmin_Should_Return_CorrectTenantIdAndUserId()
        {
            // Arrange, Act
            LoginAsHostAdmin();
            var currentTenant = GetRequiredService<ICurrentTenant>();
            var currentUser = GetRequiredService<ICurrentUser>();

            // Assert
            currentTenant.Id.ShouldBeNull();
            currentTenant.Name.ShouldBeNull();
        }

        [Fact]
        public void LoginAsDefaultTenant_Should_Return_CorrectTenantIdAndUserId()
        {
            // Arrange, Act
            LoginAsDefaultTenant();
            var currentTenant = GetRequiredService<ICurrentTenant>();
            var currentUser = GetRequiredService<ICurrentUser>();

            // Assert
            currentTenant.Id.ShouldBe(Guid.Parse(OrderManagementTestData.DefaultTenantId));
            currentTenant.Name.ShouldBe(TestData.DefaultTenantName);
            currentUser.Id.ShouldBe(TestData.DefaultTenantAdminUserId);
            currentUser.TenantId.ShouldBe(Guid.Parse(OrderManagementTestData.DefaultTenantId));
        }

Here is my service code and it's unit test which is failing.

[Authorize(OrderManagementPermissions.MENUS_MANAGEMENT)]
public class MenuAppService
    : CrudAppService<
        Menu,
        MenuDto,
        Guid,
        ListMenuRequestDto,
        CreateUpdateMenuDto>,
    IMenuAppService
{
    private readonly IRepository<Menu, Guid> _menuRepository;

    public MenuAppService(IRepository<Menu, Guid> menuRepository) : base(menuRepository)
    {
        _menuRepository = menuRepository;
        GetListPolicyName = OrderManagementPermissions.MENUS_MANAGEMENT_LIST;
        GetPolicyName = OrderManagementPermissions.MENUS_MANAGEMENT_GET;
        CreatePolicyName = OrderManagementPermissions.MENUS_MANAGEMENT_CREATE;
        UpdatePolicyName = OrderManagementPermissions.MENUS_MANAGEMENT_UPDATE;
        DeletePolicyName = OrderManagementPermissions.MENUS_MANAGEMENT_DELETE;
    }
}
public class MenusAppService_Tests : OrderManagementApplicationTestBase
{
    private IMenuAppService _menuAppService;

    public MenusAppService_Tests()
    {
        _menuAppService = GetRequiredService<IMenuAppService>();
    }

    [Fact]
    public async Task GetListAsync_Filter_Tests()
    {
        // Arrange
        LoginAsDefaultTenant();
        var listRequestDto = new ListMenuRequestDto();
        listRequestDto.RestaurantId = Guid.Parse(OrderManagementTestData.DefaultRestaurantId);

        // Act
        var list = await _menuAppService.GetListAsync(listRequestDto);

        // Assert
        list.ShouldNotBeNull();
        list.Items.Count.ShouldBe(2);
        list.Items.ShouldContain(m => m.Title == TestData.MenuKebab.Title);
        list.Items.ShouldContain(m => m.Title == TestData.MenuKotelet.Title);
    }
}

Here is the error message:

[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v2.4.3+1b45f5407b (64-bit .NET 5.0.8)
[xUnit.net 00:00:00.88]   Discovering: OrderManagement.Application.Tests
[xUnit.net 00:00:00.95]   Discovered:  OrderManagement.Application.Tests
[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v2.4.3+1b45f5407b (64-bit .NET 5.0.8)
[xUnit.net 00:00:00.87]   Starting:    OrderManagement.Application.Tests
The active test run was aborted. Reason: Test host process crashed : Stack overflow.
   at Autofac.Util.Disposable.get_IsDisposed()
   at Autofac.Core.Lifetime.LifetimeScope.IsTreeDisposed()
   at Autofac.Core.Lifetime.LifetimeScope.IsTreeDisposed()
   at Autofac.Core.Lifetime.LifetimeScope.IsTreeDisposed()
   at Autofac.Core.Lifetime.LifetimeScope.IsTreeDisposed()
   at Autofac.Core.Lifetime.LifetimeScope.IsTreeDisposed()
   at Autofac.Core.Lifetime.LifetimeScope.IsTreeDisposed()
   at Autofac.Core.Lifetime.LifetimeScope.IsTreeDisposed()
   at Autofac.Core.Lifetime.LifetimeScope.IsTreeDisposed()
   at Autofac.Core.Lifetime.LifetimeScope.IsTreeDisposed()
   at Autofac.Core.Lifetime.LifetimeScope.IsTreeDisposed()
   at Autofac.Core.Lifetime.LifetimeScope.IsTreeDisposed()
   at Autofac.Core.Lifetime.LifetimeScope.IsTreeDisposed()
   at Autofac.Core.Lifetime.LifetimeScope.IsTreeDisposed()
   at Autofac.Core.Lifetime.LifetimeScope.IsTreeDisposed()
   at Autofac.Core.Lifetime.LifetimeScope.IsTreeDisposed()
   at Autofac.Core.Lifetime.LifetimeScope.IsTreeDisposed()
   at Autofac.Core.Lifetime.LifetimeScope.IsTreeDisposed()
   at Autofac.Core.Lifetime.LifetimeScope.IsTreeDisposed()
   at Autofac.Core.Lifetime.LifetimeScope.IsTreeDisposed()
   at Autofac.Core.Lifetime.LifetimeScope.IsTreeDisposed()

However the service works fine with the swagger. Any ideas? I want to be able to run some of my tests as the host admin and some as a tenant admin/user


Solution

  • To change the current tenant id, you can use the ICurrentTenant.Change method. (https://docs.abp.io/en/abp/4.4/Multi-Tenancy#change-the-current-tenant)

    You need to use this approach in your tests, (https://github.com/abpframework/abp/blob/dev/framework/test/Volo.Abp.Features.Tests/Volo/Abp/Features/FeatureChecker_Tests.cs#L25-L28)

    For host admin you need to pass the tenantId as null.

    using (_currentTenant.Change(tenantId))
    {
       //your test logic
    }