(精华)2020年8月22日 ABP vNext 进行单元测试

现在我们在应用层中只有两个功能,增加诗人和诗人分页列表:

using Volo.Abp.Application.Services;

namespace ZL.AbpNext.Poem.Application.Poems
{
    public interface IPoemAppService:IApplicationService
    {
        /// <summary>
        /// 获取诗人分页
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        PagedResultDto<PoetDto> GetPagedPoets(PagedResultRequestDto dto);

        PoetDto AddPoet(PoetDto poet);
    }
}

在继续开发之前,我们先搭建单元测试环境,对已经开发的部分进行单元测试。

创建单元测试项目

首先我们在解决方案中创建一个“解决方案文件夹”,并命名为Test,这个文件夹下保存测试项目。Abp vNext采用xUnit作为测试框架,所以我们在这个文件夹中创建一个xUnit类型的测试项目,名称为ZL.AbpNext.Poem.Application.Test。然后在这个项目中增加项目依赖,还要使用NuGet添加volo.Abp.TestBase。所有的依赖关系如下:
在这里插入图片描述

创建测试模块

单元测试时,我们不能使用真实的数据库,我们需要使用Sqlite内存数据库进行测试。Sqlite的内存数据库中的表和数据只在数据库连接有效时起作用,如果连接关闭,数据库中的数据就丢失了。因此,需要在测试模块中进行设置,每次连接时,都需要重新创建数据库表,代码如下:

using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Text;
using Volo.Abp;
using Volo.Abp.Autofac;
using Volo.Abp.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore.Sqlite;
using Volo.Abp.Modularity;
using ZL.AbpNext.Poem.Core;
using ZL.AbpNext.Poem.EF;
using ZL.AbpNext.Poem.EF.EntityFramework;

namespace ZL.AbpNext.Poem.Application.Test
{

    [DependsOn(
    typeof(AbpAutofacModule),
    typeof(PoemCoreModule),
    typeof(PoemApplicationModule),
    typeof(PoemDataModule),
        typeof(AbpEntityFrameworkCoreSqliteModule))]
    public class PoemApplicationTestModule : AbpModule
    {
        private SqliteConnection _sqliteConnection;
        public override void ConfigureServices(ServiceConfigurationContext context)
        {
            ConfigureInMemorySqlite(context.Services);
        }
        private void ConfigureInMemorySqlite(IServiceCollection services)
        {
            _sqliteConnection = CreateDatabaseAndGetConnection();

            services.Configure<AbpDbContextOptions>(options =>
            {
                options.Configure(context =>
                {
                    context.DbContextOptions.UseSqlite(_sqliteConnection);
                });
            });
        }
        public override void OnApplicationShutdown(ApplicationShutdownContext context)
        {
            _sqliteConnection.Dispose();
        }

        private static SqliteConnection CreateDatabaseAndGetConnection()
        {
            var connection = new SqliteConnection("Data Source=:memory:");
            connection.Open();

            var options = new DbContextOptionsBuilder<PoemDbContext>()
                .UseSqlite(connection)
                .Options;

            using (var context = new PoemDbContext(options))
            {
                context.GetService<IRelationalDatabaseCreator>().CreateTables();
            }

            return connection;
        }
    }
}

创建测试基类

接下来创建测试基类,所有的测试类都从这个类派生:

using System;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp;
using Volo.Abp.Modularity;
using Volo.Abp.Uow;
using Volo.Abp.Testing;

namespace ZL.AbpNext.Poem.Application.Test
{
    public abstract class PoemTestBase<TStartupModule> : AbpIntegratedTest<TStartupModule>
        where TStartupModule : IAbpModule
    {
        protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
        {
            options.UseAutofac();
        }

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

        protected virtual async Task WithUnitOfWorkAsync(AbpUnitOfWorkOptions options, Func<Task> action)
        {
            using (var scope = ServiceProvider.CreateScope())
            {
                var uowManager = scope.ServiceProvider.GetRequiredService<IUnitOfWorkManager>();

                using (var uow = uowManager.Begin(options))
                {
                    await action();

                    await uow.CompleteAsync();
                }
            }
        }

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

        protected virtual async Task<TResult> WithUnitOfWorkAsync<TResult>(AbpUnitOfWorkOptions options, Func<Task<TResult>> func)
        {
            using (var scope = ServiceProvider.CreateScope())
            {
                var uowManager = scope.ServiceProvider.GetRequiredService<IUnitOfWorkManager>();

                using (var uow = uowManager.Begin(options))
                {
                    var result = await func();
                    await uow.CompleteAsync();
                    return result;
                }
            }
        }

    }
}

编写单元测试

现在就可以编写正式的单元测试了:

using System;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Domain.Repositories;
using Xunit;
using ZL.AbpNext.Poem.Application.Poems;
using ZL.AbpNext.Poem.Core.Poems;
using ZL.AbpNext.Poem.EF.EntityFramework;

namespace ZL.AbpNext.Poem.Application.Test
{
    public class UnitTest1:PoemTestBase<PoemApplicationTestModule>
    {
        private readonly IRepository<Poet, int> _appPoet;
        private readonly IPoemAppService _service;

        public UnitTest1()
        {
            _appPoet= GetRequiredService<IRepository<Poet, int>>();
            _service = GetRequiredService<IPoemAppService>();
        }

        [Fact]
        public async Task TestAddPoet()
        {
            await WithUnitOfWorkAsync(async () =>
            {
                var poet = new Poet
                {
                    Name = "李白",
                    Description = "诗人"

                };
                //Act
                var addedPoet = await _appPoet.InsertAsync(poet,true);
                Assert.True(_appPoet.Count() ==1);
                //Assert
                Assert.True(addedPoet.Id>0);
            });
        }

        [Fact]
        public async Task TestAddWithServicet()
        {
            await WithUnitOfWorkAsync(async () =>
            {
                var poet = new PoetDto
                {
                    Name = "李白",
                    Description = "诗人"

                };
                //Act
                var addPoet = _service.AddPoet(poet);
               
                Assert.True(addPoet.Id > 0);
                var res = _service.GetPagedPoets(new Volo.Abp.Application.Dtos.PagedResultRequestDto { MaxResultCount = 10, SkipCount = 0 });
                Assert.True(res.TotalCount == 1);

            });
        }
    }


}

在测试管理器中,可以运行和查看测试结果:
在这里插入图片描述

添加测试数据:

前面我们搭建了单元测试的框架,在单元测试中使用Sqlite的内存数据库进行测试。在每个测试用例启动的时候,都需要重新创建数据库结构,我们需要向数据库中写入一些初始数据,用于测试,这里介绍写入初始数据的方法。在测试项目中,新增加一个类负责写入初始数据,名称为PoetTestDataSeed:

using System.Threading.Tasks;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
using ZL.AbpNext.Poem.Core.Poems;

namespace ZL.AbpNext.Poem.Application.Test
{
    public class PoetTestDataSeed : IDataSeedContributor, ITransientDependency
    {
        private readonly IRepository<Poet, int> _appPoet;
        public PoetTestDataSeed(IRepository<Poet, int> _appPoet)
        {
            this._appPoet = _appPoet;
        }
        public Task SeedAsync(DataSeedContext context)
        {
            
            _appPoet.InsertAsync(new Poet
            {
                Name = "李白",
                Description = "诗人"
            }, true);

            _appPoet.InsertAsync(new Poet
            {
                Name = "杜甫",
                Description = "诗人"
            }, true);

            return Task.CompletedTask;
        }
    }
 
}

然后,修改PoemApplicationTestModule,增加初始化写入测试数据的代码:

       public override void OnApplicationInitialization(ApplicationInitializationContext context)
        {
            SeedTestData(context);
        }

        private static void SeedTestData(ApplicationInitializationContext context)
        {
            AsyncHelper.RunSync(async () =>
            {
                using (var scope = context.ServiceProvider.CreateScope())
                {
                    await scope.ServiceProvider
                        .GetRequiredService<IDataSeeder>()
                        .SeedAsync();
                }
            });
        }

这时再次运行测试,发现出现测试错误,这是因为数据库中的记录个数大于1,我们将原来的代码修改为:

Assert.True(_appPoet.Count() >1);

再次运行,测试通过了。

©️2020 CSDN 皮肤主题: 猿与汪的秘密 设计师:上身试试 返回首页