从零开始进行ABP项目开发(五)——单元测试

我们创建单元测试对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 中打开测试资源管理器窗口(测试->窗口->测试资源管理器):


从零开始进行ABP项目开发(五)——单元测试_第1张图片
测试资源管理器

测试资源管理器中列出了我们刚刚编写的测试用例:


从零开始进行ABP项目开发(五)——单元测试_第2张图片
测试列表

执行运行测试,可以看到运行结果:


从零开始进行ABP项目开发(五)——单元测试_第3张图片
测试结果

下一步工作

到这里,我们已经完成了单元测试项目的基本框架搭建工作,下面要回过头来,将应用层完成,并使用单元测试进行测试。

本文同步发布在我的个人网站 http://www.jiagoushi.cn

你可能感兴趣的:(从零开始进行ABP项目开发(五)——单元测试)