【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测试类库,点击运行测试,测试用例全部运行通过,测试结果如下

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

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

浙公网安备 33010602011771号