【ABP】项目示例(6)——单元测试

单元测试

在上一章节中,已经实现了应用服务,在这一章节中,对仓储层和应用层进行单元测试
创建名称为General.Backend.TestBase的Xunit测试类库,配置测试模块功能需要使用到的工作单元和依赖注入系统
在程序包管理控制台选中General.Backend.TestBase,执行以下命令安装ABP测试和断言相关的Nuget包

Install-Package Shouldly -v 4.2.1
Install-Package Volo.Abp.TestBase -v 8.3.0
Install-Package Volo.Abp.Autofac -v 8.3.0

General.Backend.TestBase添加项目引用General.Backend.Domain
新建名称为GeneralTestBaseModule的基础测试模块类

[DependsOn(
    typeof(AbpAutofacModule),
    typeof(AbpTestBaseModule),
    typeof(GeneralDomainModule)
    )]
public class GeneralTestBaseModule : AbpModule
{
}

新建名称为GeneralTestBase的基础测试抽象类,供各个模块的单元测试使用通用的工作单元

public abstract class GeneralTestBase<TStartupModule> : AbpIntegratedTest<TStartupModule>
    where TStartupModule : IAbpModule
{
    protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
    {
        options.UseAutofac();
    }

    protected virtual async Task WithUnitOfWorkAsync(AbpUnitOfWorkOptions options, Func<Task> func)
    {
        using (var scope = ServiceProvider.CreateAsyncScope())
        {
            var uowManager = scope.ServiceProvider.GetRequiredService<IUnitOfWorkManager>();
            using (var uow = uowManager.Begin(options))
            {
                await func();
                await uow.CompleteAsync();
            }
        }
    }

    protected virtual async Task<TResult> WithUnitOfWorkAsync<TResult>(AbpUnitOfWorkOptions options, Func<Task<TResult>> func)
    {
        using (var scope = ServiceProvider.CreateAsyncScope())
        {
            var uowManager = scope.ServiceProvider.GetRequiredService<IUnitOfWorkManager>();
            using (var uow = uowManager.Begin(options))
            {
                var result = await func();
                await uow.CompleteAsync();
                return result;
            }
        }
    }

    protected virtual async Task WithUnitOfWorkAsync(Func<Task> func)
    {
        await WithUnitOfWorkAsync(new AbpUnitOfWorkOptions(), func);
    }

    protected virtual async Task<TResult> WithUnitOfWorkAsync<TResult>(Func<Task<TResult>> func)
    {
        return await WithUnitOfWorkAsync(new AbpUnitOfWorkOptions(), func);
    }
}

仓储层单元测试

创建名称为General.Backend.EntityFrameworkCore.Tests的Xunit测试类库,以及在该类库中新建名称为UnitTests的文件夹,用于测试仓储层功能
在程序包管理控制台选中General.Backend.EntityFrameworkCore.Tests,执行以下命令安装Sqlite数据库相关的Nuget包

Install-Package Volo.Abp.EntityFrameworkCore.Sqlite -v 8.3.0

General.Backend.EntityFrameworkCore.Tests添加项目引用General.Backend.TestBase和General.Backend.EntityFrameworkCore
新建名称为GeneralEntityFrameworkCoreTestModule的仓储测试模块类,配置Sqlite作为测试的数据库

[DependsOn(
    typeof(AbpEntityFrameworkCoreSqliteModule),
    typeof(GeneralTestBaseModule),
    typeof(GeneralEntityFrameworkCoreModule)
    )]
public class GeneralEntityFrameworkCoreTestModule : AbpModule
{
    private SqliteConnection? _sqliteConnection;

    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        _sqliteConnection = CreateDatabaseAndGetConnection();
        Configure<AbpDbContextOptions>(options =>
        {
            options.Configure(config =>
            {
                config.DbContextOptions.UseSqlite(_sqliteConnection);
            });
        });

    }

    public override void OnApplicationShutdown(ApplicationShutdownContext context)
    {
        _sqliteConnection?.Dispose();
    }

    public static SqliteConnection CreateDatabaseAndGetConnection()
    {
        var connection = new SqliteConnection("Data Source=:memory:");
        connection.Open();
        var options = new DbContextOptionsBuilder<GeneralDbContext>()
            .UseSqlite(connection)
            .Options;
        using var context = new GeneralDbContext(options);
        context.GetService<IRelationalDatabaseCreator>().CreateTables();
        return connection;
    }
}

新建名称为StoreEntityFrameworkCoreTestBase的仓储测试抽象类,供仓储单元测试中的各个单元测试类继承使用

public abstract class GeneralEntityFrameworkCoreTestBase : GeneralTestBase<GeneralEntityFrameworkCoreTestModule>
{

}

在UnitTests文件夹中新建名称为UserRepositoryTests和RoleRepositoryTests的单元测试类

  • UserRepositoryTests
public class UserRepositoryTests : GeneralEntityFrameworkCoreTestBase
{
    private readonly IUserRepository _userRepository;
    private readonly IGuidGenerator _guidGenerator;

    public UserRepositoryTests()
    {
        _userRepository = GetRequiredService<IUserRepository>();
        _guidGenerator = GetRequiredService<IGuidGenerator>();
    }

    [Fact]
    public async Task Should_Insert_A_Valid_User()
    {
        var user = new User(
            _guidGenerator.Create(),
            "hui",
            "1q2w3e4r%",
            "Hui");

        await WithUnitOfWorkAsync(async () =>
        {
            await _userRepository.InsertAsync(user);
        });
        var result = await WithUnitOfWorkAsync(async () =>
        {
            return await _userRepository.FirstOrDefaultAsync(item => item.Id == user.Id);
        });
        
        result.ShouldNotBeNull();
    }

    [Fact]
    public async Task Should_Update_User_With_Name()
    {
        var user = new User(
            _guidGenerator.Create(),
            "hui",
            "1q2w3e4r%",
            "Hui");

        await _userRepository.InsertAsync(user);
        user = await _userRepository.GetAsync(item => item.Id == user.Id);
        user.SetName("Rain");
        await _userRepository.UpdateAsync(user);
        var updatedUser = await _userRepository.FindAsync(item => item.Name == "Rain");

        updatedUser.ShouldNotBeNull();
    }

    [Fact]
    public async Task Should_Insert_Some_Valid_User()
    {
        var user1 = new User(
            _guidGenerator.Create(),
            "hui",
            "1q2w3e4r%",
            "Hui",
            "00-12300",
            "深圳");
        var user2 = new User(
            _guidGenerator.Create(),
            "rain",
            "p0o9i8u7^",
            "Rain",
            "00-12400");

        await WithUnitOfWorkAsync(async () =>
        {
            await _userRepository.InsertAsync(user1);
            await _userRepository.InsertAsync(user2);
        });
        var result = await _userRepository.GetListAsync();

        result.Count.ShouldBe(2);
    }

    [Fact]
    public async Task Should_Delete_Special_User()
    {
        var users = new List<User>()
        {
            new(
                _guidGenerator.Create(),
                "hui",
                "1q2w3e4r%",
                "Hui",
                "00-12300",
                "深圳"),
            new(
                _guidGenerator.Create(),
                "rain",
                "p0o9i8u7^",
                "Rain",
                "00-12400")
        };

        await WithUnitOfWorkAsync(async () =>
        {
            await _userRepository.InsertManyAsync(users);
        });
        await WithUnitOfWorkAsync(async () =>
        {
            await _userRepository.DeleteAsync(user => user.Name == "Admin");
        });
        var deletedUser = await WithUnitOfWorkAsync(async () =>
        {
            return await _userRepository.FirstOrDefaultAsync(user => user.Name == "Admin");
        });
        var notDeletedUser = await WithUnitOfWorkAsync(async () =>
        {
            return await _userRepository.FirstOrDefaultAsync(user => user.Name == "Rain");
        });

        deletedUser.ShouldBeNull();
        notDeletedUser.ShouldNotBeNull();
    }
}
  • RoleRepositoryTests
public class RoleRepositoryTests : GeneralEntityFrameworkCoreTestBase
{
    private readonly IRoleRepository _roleRepository;
    private readonly IGuidGenerator _guidGenerator;

    public RoleRepositoryTests()
    {
        _roleRepository = GetRequiredService<IRoleRepository>();
        _guidGenerator = GetRequiredService<IGuidGenerator>();
    }

    [Fact]
    public async Task Should_Insert_A_Valid_Role()
    {
        var role = new Role(
            _guidGenerator.Create(),
            "Rain",
            "Rain",
            "管理员");

        await _roleRepository.InsertAsync(role);
        var result = await _roleRepository.FindAsync(item => item.Id == role.Id);

        result.ShouldNotBeNull();
    }

    [Fact]
    public async Task Should_Delete_All_Role()
    {
        var roles = new List<Role>(){
            new (
                _guidGenerator.Create(),
                "Rain",
                "Rain",
                "管理员"),
            new (
                _guidGenerator.Create(),
                "Boss",
                "Boss",
                "头儿")
        };

        await _roleRepository.InsertManyAsync(roles);
        var result = await _roleRepository.GetPagedListAsync(0, 1, string.Empty);
        result.Count.ShouldBe(1);

        await _roleRepository.DeleteManyAsync(roles);
        var role = await _roleRepository.FindAsync(item => roles.Select(role => role.Id).Contains(item.Id));
        role.ShouldBeNull();
    }
}

针对General.Backend.EntityFrameworkCore.Tests测试类库,点击运行测试,测试用例全部运行通过,测试结果如下

应用层单元测试

创建名称为General.Backend.Application.Tests的Xunit测试类库,以及在该类库中新建名称为UnitTests的文件夹,用于测试应用层功能
General.Backend.Application.Tests添加项目引用General.Backend.Application和General.Backend.EntityFrameworkCore.Tests
新建名称为GeneralApplicationTestModule的应用测试模块类

[DependsOn(
    typeof(GeneralEntityFrameworkCoreTestModule),
    typeof(GeneralApplicationModule))]
public class GeneralApplicationTestModule : AbpModule
{

}

新建名称为GeneralApplicationTestBase的应用测试抽象类,供应用单元测试中的各个单元测试类继承使用

public abstract class GeneralApplicationTestBase : GeneralTestBase<GeneralApplicationTestModule>
{

}

在UnitTests文件夹中新建名称为UserAppServiceTests的单元测试类

  • UserAppServiceTests
public class UserAppServiceTests : GeneralApplicationTestBase
{
    private readonly IUserAppService _userAppService;
    
    public UserAppServiceTests() 
    {
        _userAppService = GetRequiredService<IUserAppService>();
    }

    [Fact]
    public async Task Should_Create_A_Valid_User()
    {
        var input = new UserCreateDto
        {
            Account = "hui",
            Password = "q1w2e3r4%",
            Name = "Hui",
            Address = "深圳",
            Contact = null
        };
        await Should.NotThrowAsync(async () =>
        {
            await _userAppService.CreateAsync(input);
        });
    }

    [Fact]
    public async Task Should_Update_A_Valid_User()
    {
        var createInput = new UserCreateDto
        {
            Account = "rain",
            Password = "p0o9i8u7^",
            Name = "Rain",
            Address = "深圳",
            Contact = "0023-684"
        };
        await Should.NotThrowAsync(async () =>
        {
            var userDto = await _userAppService.CreateAsync(createInput);
            var updateInput = new UserUpdateDto
            {
                Id = userDto.Id,
                Password = userDto.Password,
                Name = userDto.Name,
                Address = "广州",
                Contact = "0025-797"
            };
            await _userAppService.UpdateAsync(updateInput);
        });
    }

    [Fact]
    public async Task Should_Delete_A_User()
    {
        var createInput = new UserCreateDto
        {
            Account = "rain",
            Password = "p0o9i8u7^",
            Name = "Rain",
            Address = "深圳",
            Contact = "0023-684"
        };
        var userDto = new UserDto();
        await Should.NotThrowAsync(async () =>
        {
            userDto = await _userAppService.CreateAsync(createInput);
            await _userAppService.DeleteAsync([ userDto.Id ]);
            
        });
        await Should.ThrowAsync<UserFriendlyException>(async () =>
        {
            await _userAppService.GetAsync(userDto.Id);
        });
    }

    [Fact]
    public async Task Should_Get_Some_User()
    {
        var createInput1 = new UserCreateDto
        {
            Account = "hui",
            Password = "q1w2e3r4%",
            Name = "Hui",
            Address = "深圳",
            Contact = "0023-684"
        };
        var createInput2 = new UserCreateDto
        {
            Account = "rain",
            Password = "p0o9i8u7^",
            Name = "Rain",
            Address = "广州",
            Contact = "0018-461"
        };

        var result = await WithUnitOfWorkAsync(async () =>
        {
            await Should.NotThrowAsync(async () =>
            {
                await _userAppService.CreateAsync(createInput1);
                await _userAppService.CreateAsync(createInput2);
            });
            return await _userAppService.GetListAsync(new UserQueryDto
            {
                Name = "Rain",
                PageSize = 10,
                PageIndex = 1
            });
        });

        result.TotalCount.ShouldBe(1);
    }
}

针对General.Backend.Application.Tests测试类库,点击运行测试,测试用例全部运行通过,测试结果如下

解决方案的目录结构现如下

在下一章节中,进行数据播种

posted @ 2025-01-15 23:52  loveraindeme  阅读(32)  评论(0)    收藏  举报