我们创建单元测试对ZL.Poem.Application进行测试
创建单元测试项目
Abp采用xunit和Shouldly辅助进行单元测试。首先我们在解决方案中创建一个“解决方案文件夹”,并命名为Test,这个文件夹下保存测试项目。注意,这个文件夹并不存在于文件系统,只是在解决方案中为了分类使用的虚拟文件夹。
然后在这个文件夹中增加一个.Net Core 类库项目,命名为ZL.PoemApplication.Test。然后添加ZL.Poem.Application和ZL.Poem.EF到依赖项。还要通过NuGet添加Abp.TestBase、Microsoft.EntityFrameworkCore.InMemory、 Castle.Core、Castle.Windsor.MsDependencyInjection、xunit、xunit.runner.visualstudio、xunit.extensibility.execution、Shouldly
创建Abp 测试模块
然后,我们创建Abp测试模块PoemApplicationTestModule。
using Abp.Modules;
using Abp.Reflection.Extensions;
using Abp.TestBase;
using Castle.MicroKernel.Registration;
using Castle.Windsor.MsDependencyInjection;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using ZL.Poem.Application;
using ZL.Poem.EF;
using ZL.Poem.EF.EntityFramework;
namespace ZL.PoemApplication.Test
{
[DependsOn(
typeof(PoemApplicationModule),
typeof(PoemDataModule),
typeof(AbpTestBaseModule)
)]
public class PoemApplicationTestModule : AbpModule
{
public PoemApplicationTestModule(PoemDataModule poemDataModule)
{
//跳过DbContext注册
poemDataModule.SkipDbContextRegistration = true;
}
public override void PreInitialize()
{
Configuration.UnitOfWork.IsTransactional = false; //EF Core InMemory DB does not support transactions.
SetupInMemoryDb();
}
public override void Initialize()
{
IocManager.RegisterAssemblyByConvention(typeof(PoemApplicationTestModule).GetAssembly());
}
private void SetupInMemoryDb()
{
var services = new ServiceCollection()
.AddEntityFrameworkInMemoryDatabase();
var serviceProvider = WindsorRegistrationHelper.CreateServiceProvider(
IocManager.IocContainer,
services
);
var builder = new DbContextOptionsBuilder();
builder.UseInMemoryDatabase().UseInternalServiceProvider(serviceProvider);
IocManager.IocContainer.Register(
Component
.For>()
.Instance(builder.Options)
.LifestyleSingleton()
);
}
}
}
这里,配置使用内存数据库代替实际数据库,确保不会因为数据库中数据的变化导致测试不通过。
创建测试数据
由于我们使用内存数据库,因此需要在测试项目中创建一些测试数据。创建一个文件夹,命名为TestData,在这个文件夹中创建一个类名称为TestDataBuilder:
using ZL.Poem.Core.Poems;
using ZL.Poem.EF.EntityFramework;
namespace ZL.PoemApplication.Test.TestData
{
public class TestDataBuilder
{
private readonly PoemDbContext _context;
public TestDataBuilder(PoemDbContext context)
{
_context = context;
}
public void Build()
{
_context.Poets.AddRange(
new Poet { Name = "李白" },
new Poet { Name = "杜甫" }
);
}
}
}
这里我们添加两个诗人。
创建测试基类
接下来,我们创建一个测试基类,用于处理测试的通用方法:
using Abp.TestBase;
using System;
using System.Threading.Tasks;
using ZL.Poem.EF.EntityFramework;
using ZL.PoemApplication.Test.TestData;
namespace ZL.PoemApplication.Test
{
public class PoemTestBase : AbpIntegratedTestBase
{
public PoemTestBase()
{
UsingDbContext(context => new TestDataBuilder(context).Build());
}
protected virtual void UsingDbContext(Action action)
{
using (var context = LocalIocManager.Resolve())
{
action(context);
context.SaveChanges();
}
}
protected virtual T UsingDbContext(Func func)
{
T result;
using (var context = LocalIocManager.Resolve())
{
result = func(context);
context.SaveChanges();
}
return result;
}
protected virtual async Task UsingDbContextAsync(Func action)
{
using (var context = LocalIocManager.Resolve())
{
await action(context);
await context.SaveChangesAsync(true);
}
}
protected virtual async Task UsingDbContextAsync(Func> func)
{
T result;
using (var context = LocalIocManager.Resolve())
{
result = await func(context);
context.SaveChanges();
}
return result;
}
}
}
创建测试
在测试基类的基础上,可以创建测试类:
using Shouldly;
using Xunit;
using ZL.Poem.Application.Poems;
namespace ZL.PoemApplication.Test
{
public class PoemApplicationTest : PoemTestBase
{
private readonly IPoemAppService _appService;
public PoemApplicationTest()
{
_appService = Resolve();
}
[Fact]
public void GetPoets_Test()
{
// Act
var output = _appService.GetPagedPoets(new PagedResultRequestDto { MaxResultCount = 20, SkipCount = 0 });
// Assert
output.Items.Count.ShouldBe(2);
}
}
}
运行测试
这时,我们可以在Visual Studio 中打开测试资源管理器窗口(测试->窗口->测试资源管理器):
测试资源管理器中列出了我们刚刚编写的测试用例:
执行运行测试,可以看到运行结果:
下一步工作
到这里,我们已经完成了单元测试项目的基本框架搭建工作,下面要回过头来,将应用层完成,并使用单元测试进行测试。
本文同步发布在我的个人网站 http://www.jiagoushi.cn