ASP.NET Core项目使用xUnit进行单元测试

目录
  • 一、前言
  • 二、创建示例项目
    • 1、UnitTest.Model
    • 2、UnitTest.Data
    • 3、UnitTest.IRepository
    • 4、UnitTest.Repository
    • 5、UnitTestDemo
    • 6、TestDemo
  • 三、编写单元测试
    • 1、运行单元测试
    • 2、调试单元测试

一、前言

在以前的.NET Framework项目中,我们也写过一些单元测试的项目,而在ASP.NET Core 这种Web或者API应用程序中要做单元测试是很方便的。

这篇文章主要讲解如何使用xUnit对ASP.NET Core应用程序做单元测试。.NET Core中常用的测试工具还有NUnit和MSTest。

xUnit是一个测试框架,可以针对.net/.net core项目进行测试。测试项目需要引用被测试的项目,从而对其进行测试。测试项目同时需要引用xUnit库。测试编写好后,用Test Runner来运行测试。Test Runner可以读取测试代码,并且会知道我们所使用的测试框架,然后执行,并显示结果。目前可用的Test Runner包括vs自带的Test Explorer,或者dotnet core命令行,以及第三方工具,例如resharper等。

xUnit可以支持多种平台的测试:

  • .NET Framework
  • .NET Core
  • .NET Standard
  • UWP
  • Xamarin

二、创建示例项目

为了使示例项目更加的贴近真实的项目开发,这里采用分层的方式创建一个示例项目,创建完成后的项目结构如下图所示:

下面讲解一下每层的作用,按照从上往下的顺序:

  • TestDemo:从名字就可以看出来,这是一个单元测试的项目,针对控制器进行测试。
  • UnitTest.Data:数据访问,封装与EntityFrameworkCore相关的操作。
  • UnitTest.IRepository:泛型仓储接口,封装基础的增删改查。
  • UnitTest.Model:实体层,定义项目中使用到的所有实体。
  • UnitTest.Repository:泛型仓储接口实现层,实现接口里面定义的方法。
  • UnitTestDemo:ASP.NET Core WebApi,提供API接口。

1、UnitTest.Model

实体层里面只有一个Student类:

using System;
using System.Collections.Generic;
using System.Text;

namespace UnitTest.Model
{
    public class Student
    {
        public int ID { get; set; }

        public string Name { get; set; }

        public int Age { get; set; }

        public string Gender { get; set; }
    }
}

2、UnitTest.Data

里面封装与EF Core有关的操作,首先需要引入Microsoft.EntityFrameworkCore、Microsoft.EntityFrameworkCore.SqlServer、Microsoft.EntityFrameworkCore.Tools三个NuGet包,直接在管理NuGet程序包里面引入,这里不在讲述。

引入相关NuGet包以后,我们创建数据上下文类,该类继承自EF Core的DbContext,里面设置表名和一些属性:

using Microsoft.EntityFrameworkCore;
using UnitTest.Model;

namespace UnitTest.Data
{
    /// <summary>
    /// 数据上下文类
    /// </summary>
    public class AppDbContext : DbContext
    {
        /// <summary>
        /// 通过构造函数给父类构造传参
        /// </summary>
        /// <param name="options"></param>
        public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
        {

        }

        public DbSet<Student> Students { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Student>().ToTable("T_Student");
            modelBuilder.Entity<Student>().HasKey(p => p.ID);
            modelBuilder.Entity<Student>().Property(p => p.Name).HasMaxLength(32);

            // 添加种子数据
            modelBuilder.Entity<Student>().HasData(
                new Student()
                {
                    ID = 1,
                    Name = "测试1",
                    Age = 20,
                    Gender = "男"
                },
                new Student()
                {
                    ID = 2,
                    Name = "测试2",
                    Age = 22,
                    Gender = "女"
                },
                new Student()
                {
                    ID = 3,
                    Name = "测试3",
                    Age = 23,
                    Gender = "男"
                });
            base.OnModelCreating(modelBuilder);
        }
    }
}

这里采用数据迁移的方式生成数据库,需要在API项目中引入Microsoft.EntityFrameworkCore、Microsoft.EntityFrameworkCore.SqlServer、Microsoft.EntityFrameworkCore.Tools三个NuGet包。引入方式同上。

然后在API项目的appsettings.json文件里面添加数据库链接字符串:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  // 数据库连接字符串
  "ConnectionString": {
    "DbConnection": "Initial Catalog=TestDb;User Id=sa;Password=1234;Data Source=.;Connection Timeout=10;"
  }
}

在JSON文件中添加完连接字符串以后,修改Startup类的ConfigureServices方法,在里面配置使用在json文件中添加的连接字符串:

// 添加数据库连接字符串
services.AddDbContext<AppDbContext>(options =>
{
    options.UseSqlServer(Configuration.GetSection("ConnectionString").GetSection("DbConnection").Value);
});

这样就可以使用数据迁移的方式生成数据库了。

3、UnitTest.IRepository

该项目中使用泛型仓储,定义一个泛型仓储接口:

using System.Collections.Generic;
using System.Threading.Tasks;

namespace UnitTest.IRepository
{
    public interface IRepository<T> where T:class,new()
    {
        Task<List<T>> GetList();

        Task<int?> Add(T entity);

        Task<int?> Update(T entity);

        Task<int?> Delete(T entity);
    }
}

然后在定义IStudentRepository接口继承自IRepository泛型接口:

using UnitTest.Model;

namespace UnitTest.IRepository
{
    public interface IStudentRepository: IRepository<Student>
    {
    }
}

4、UnitTest.Repository

这里是实现上面定义的仓储接口:

using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using UnitTest.Data;
using UnitTest.IRepository;
using UnitTest.Model;

namespace UnitTest.Repository
{
    public class StudentRepository : IStudentRepository
    {
        private readonly AppDbContext _dbContext;

        /// <summary>
        /// 通过构造函数实现依赖注入
        /// </summary>
        /// <param name="dbContext"></param>
        public StudentRepository(AppDbContext dbContext)
        {
            _dbContext = dbContext;
        }

        public async Task<int?> Add(Student entity)
        {
            _dbContext.Students.Add(entity);
            return await _dbContext.SaveChangesAsync();
        }

        public async Task<int?> Delete(Student entity)
        {
            _dbContext.Students.Remove(entity);
            return await _dbContext.SaveChangesAsync();
        }

        public async Task<List<Student>> GetList()
        {
            List<Student> list = new List<Student>();

            list = await Task.Run<List<Student>>(() =>
            {
                return _dbContext.Students.ToList();
            });

            return list;
        }

        public async Task<int?> Update(Student entity)
        {
            Student student = _dbContext.Students.Find(entity.ID);
            if (student != null)
            {
                student.Name = entity.Name;
                student.Age = entity.Age;
                student.Gender = entity.Gender;
                _dbContext.Entry<Student>(student).State = Microsoft.EntityFrameworkCore.EntityState.Modified;
                return await _dbContext.SaveChangesAsync();
            }
            return 0;
        }
    }
}

5、UnitTestDemo

先添加一个Value控制器,里面只有一个Get方法,而且没有任何的依赖关系,先进行最简单的测试:

using Microsoft.AspNetCore.Mvc;

namespace UnitTestDemo.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ValueController : ControllerBase
    {
        [HttpGet("{id}")]
        public ActionResult<string> Get(int id)
        {
            return $"Para is {id}";
        }
    }
}

6、TestDemo

我们在添加测试项目的时候,直接选择使用xUnit测试项目,如下图所示:

这样项目创建完成以后,就会自动添加xUnit的引用:

  <ItemGroup>
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
    <PackageReference Include="xunit" Version="2.4.1" />
    <PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
  </ItemGroup>

但要测试 ASP.NET Core 应用还需要添加两个 NuGet 包:

Install-Package Microsoft.AspNetCore.App
Install-Package Microsoft.AspNetCore.TestHost

上面是使用命令的方式进行安装,也可以在管理NuGet程序包里面进行搜索,然后安装。

千万不要忘记还要引入要测试的项目。最后的项目引入是这样的:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>

    <IsPackable>false</IsPackable>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.App" Version="2.2.8" />
    <PackageReference Include="Microsoft.AspNetCore.TestHost" Version="3.1.2" />
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
    <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
    <PackageReference Include="xunit" Version="2.4.1" />
    <PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
    <PackageReference Include="coverlet.collector" Version="1.0.1" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\UnitTest.Model\UnitTest.Model.csproj" />
    <ProjectReference Include="..\UnitTestDemo\UnitTestDemo.csproj" />
  </ItemGroup>

</Project>

都添加完以后,重新编译项目,保证生成没有错误。

三、编写单元测试

单元测试按照从上往下的顺序,一般分为三个阶段:

  • Arrange:准备阶段。这个阶段做一些准备工作,例如创建对象实例,初始化数据等。
  • Act:行为阶段。这个阶段是用准备好的数据去调用要测试的方法。
  • Assert:断定阶段。这个阶段就是把调用目标方法的返回值和预期的值进行比较,如果和预期值一致则测试通过,否则测试失败。

我们在API项目中添加了一个Value控制器,我们以Get方法作为测试目标。一般一个单元测试方法就是一个测试用例。

我们在测试项目中添加一个ValueTest测试类,然后编写一个单元测试方法,这里是采用模拟HTTPClient发送Http请求的方式进行测试:

using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using UnitTestDemo;
using Xunit;

namespace TestDemo
{
    public class ValueTests
    {
        public HttpClient _client { get; }

        /// <summary>
        /// 构造方法
        /// </summary>
        public ValueTests()
        {
            var server = new TestServer(WebHost.CreateDefaultBuilder()
           .UseStartup<Startup>());
            _client = server.CreateClient();
        }

        [Fact]
        public async Task GetById_ShouldBe_Ok()
        {
            // 1、Arrange
            var id = 1;

            // 2、Act
            // 调用异步的Get方法
            var response = await _client.GetAsync($"/api/value/{id}");

            // 3、Assert
            Assert.Equal(HttpStatusCode.OK, response.StatusCode);

        }
    }
}

我们在构造函数中,通过TestServer拿到一个HttpClient对象,用它来模拟Http请求。我们写了一个测试用例,完整演示了单元测试的Arrange、Act和Assert三个步骤。

1、运行单元测试

单元测试用例写好以后,打开“测试资源管理器”:

在底部就可以看到测试资源管理器了:

在要测试的方法上面右键,选择“运行测试”就可以进行测试了:

注意观察测试方法前面图标的颜色,目前是蓝色的,表示测试用例还没有运行过:

测试用例结束以后,我们在测试资源管理器里面可以看到结果:

绿色表示测试通过。我们还可以看到执行测试用例消耗的时间。

如果测试结果和预期结果一致,那么测试用例前面图标的颜色也会变成绿色:

如果测试结果和预期结果不一致就会显示红色,然后需要修改代码直到出现绿色图标。我们修改测试用例,模拟测试失败的情况:

using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using UnitTestDemo;
using Xunit;

namespace TestDemo
{
    public class ValueTests
    {
        public HttpClient _client { get; }

        /// <summary>
        /// 构造方法
        /// </summary>
        public ValueTests()
        {
            var server = new TestServer(WebHost.CreateDefaultBuilder()
           .UseStartup<Startup>());
            _client = server.CreateClient();
        }

        [Fact]
        public async Task GetById_ShouldBe_Ok()
        {
            // 1、Arrange
            var id = 1;

            // 2、Act
            // 调用异步的Get方法
            var response = await _client.GetAsync($"/api/value/{id}");

            //// 3、Assert
            //Assert.Equal(HttpStatusCode.OK, response.StatusCode);

            // 3、Assert
            // 模拟测试失败
            Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);

        }
    }
}

然后运行测试用例:

2、调试单元测试

我们也可以通过添加断点的方式在测试用例中进行调试。调试单元测试很简单,只需要在要调试的方法上面右键选择“调试测试”,如下图所示:

其它操作就跟调试普通方法一样。

除了添加断点调试,我们还可以采用打印日志的方法来快速调试,xUnit可以很方便地做到这一点。我们修改ValueTest类:

using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using UnitTestDemo;
using Xunit;
using Xunit.Abstractions;

namespace TestDemo
{
    public class ValueTests
    {
        public HttpClient _client { get; }
        public ITestOutputHelper Output { get; }

        /// <summary>
        /// 构造方法
        /// </summary>
        public ValueTests(ITestOutputHelper outputHelper)
        {
            var server = new TestServer(WebHost.CreateDefaultBuilder()
           .UseStartup<Startup>());
            _client = server.CreateClient();
            Output = outputHelper;
        }

        [Fact]
        public async Task GetById_ShouldBe_Ok()
        {
            // 1、Arrange
            var id = 1;

            // 2、Act
            // 调用异步的Get方法
            var response = await _client.GetAsync($"/api/value/{id}");

            // 3、Assert
            // 模拟测试失败
            //Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);

            // 输出返回信息
            // Output
            var responseText = await response.Content.ReadAsStringAsync();
            Output.WriteLine(responseText);

            // 3、Assert
            Assert.Equal(HttpStatusCode.OK, response.StatusCode);

        }
    }
}

这里我们在构造函数中添加了 ITestOutputHelper 参数,xUnit 会将一个实现此接口的实例注入进来。拿到这个实例后,我们就可以用它来输出日志了。运行(注意不是 Debug)此方法,运行结束后在测试资源管理器里面查看:

点击就可以看到输出的日志了:

在上面的例子中,我们是使用的简单的Value控制器进行测试,控制器里面没有其他依赖关系,如果控制器里面有依赖关系该如何测试呢?方法还是一样的,我们新建一个Student控制器,里面依赖IStudentRepository接口,代码如下:

using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using UnitTest.IRepository;
using UnitTest.Model;

namespace UnitTestDemo.Controllers
{
    [Route("api/student")]
    [ApiController]
    public class StudentController : ControllerBase
    {
        private readonly IStudentRepository _repository;

        /// <summary>
        /// 通过构造函数注入
        /// </summary>
        /// <param name="repository"></param>
        public StudentController(IStudentRepository repository)
        {
            _repository = repository;
        }

        /// <summary>
        /// get方法
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public async Task<ActionResult<List<Student>>> Get()
        {
            return await _repository.GetList();
        }
    }
}

然后在Startup类的ConfigureServices方法中注入:

public void ConfigureServices(IServiceCollection services)
{
    // 添加数据库连接字符串
    services.AddDbContext<AppDbContext>(options =>
    {
        options.UseSqlServer(Configuration.GetSection("ConnectionString").GetSection("DbConnection").Value);
    });
    // 添加依赖注入到容器中
    services.AddScoped<IStudentRepository, StudentRepository>();
    services.AddControllers();
}

在单元测试项目中添加StudentTest类:

using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using UnitTest.Model;
using UnitTestDemo;
using Xunit;
using Xunit.Abstractions;

namespace TestDemo
{
    public class StudentTest
    {
        public HttpClient Client { get; }
        public ITestOutputHelper Output { get; }
        public StudentTest(ITestOutputHelper outputHelper)
        {
            var server = new TestServer(WebHost.CreateDefaultBuilder()
           .UseStartup<Startup>());
            Client = server.CreateClient();
            Output = outputHelper;
        }

        [Fact]
        public async Task Get_ShouldBe_Ok()
        {
            // 2、Act
            var response = await Client.GetAsync($"api/student");

            // Output
            string context = await response.Content.ReadAsStringAsync();
            Output.WriteLine(context);
            List<Student> list = JsonConvert.DeserializeObject<List<Student>>(context);

            // Assert
            Assert.Equal(3, list.Count);
        }
    }
}

然后运行单元测试:

可以看到,控制器里面如果有依赖关系,也是可以使用这种方式进行测试的。

Post方法也可以使用同样的方式进行测试,修改控制器,添加Post方法:

using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using UnitTest.IRepository;
using UnitTest.Model;

namespace UnitTestDemo.Controllers
{
    [Route("api/student")]
    [ApiController]
    public class StudentController : ControllerBase
    {
        private readonly IStudentRepository _repository;

        /// <summary>
        /// 通过构造函数注入
        /// </summary>
        /// <param name="repository"></param>
        public StudentController(IStudentRepository repository)
        {
            _repository = repository;
        }

        /// <summary>
        /// get方法
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public async Task<ActionResult<List<Student>>> Get()
        {
            return await _repository.GetList();
        }

        /// <summary>
        /// Post方法
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<bool> Post([FromBody]Student entity)
        {
            int? result = await _repository.Add(entity);
            if(result==null)
            {
                return false;
            }
            else
            {
                return result > 0 ? true : false;
            }

        }
    }
}

在增加一个Post的测试方法:

using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using UnitTest.Model;
using UnitTestDemo;
using Xunit;
using Xunit.Abstractions;

namespace TestDemo
{
    public class StudentTest
    {
        public HttpClient Client { get; }
        public ITestOutputHelper Output { get; }
        public StudentTest(ITestOutputHelper outputHelper)
        {
            var server = new TestServer(WebHost.CreateDefaultBuilder()
           .UseStartup<Startup>());
            Client = server.CreateClient();
            Output = outputHelper;
        }

        [Fact]
        public async Task Get_ShouldBe_Ok()
        {
            // 2、Act
            var response = await Client.GetAsync($"api/student");

            // Output
            string context = await response.Content.ReadAsStringAsync();
            Output.WriteLine(context);
            List<Student> list = JsonConvert.DeserializeObject<List<Student>>(context);

            // Assert
            Assert.Equal(3, list.Count);
        }

        [Fact]
        public async Task Post_ShouldBe_Ok()
        {
            // 1、Arrange
            Student entity = new Student()
            {
             Name="测试9",
             Age=25,
             Gender="男"
            };

            var str = JsonConvert.SerializeObject(entity);
            HttpContent content = new StringContent(str);

            // 2、Act
            content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");

            HttpResponseMessage response = await Client.PostAsync("api/student", content);
            string responseBody = await response.Content.ReadAsStringAsync();
            Output.WriteLine(responseBody);

            // 3、Assert
            Assert.Equal("true", responseBody);
        }
    }
}

运行测试用例:

这样一个简单的单元测试就完成了。

我们观察上面的两个测试类,发现这两个类都有一个共同的特点:都是在构造函数里面创建一个HttpClient对象,我们可以把创建HttpClient对象抽离到一个共同的基类里面,所有的类都继承自基类。该基类代码如下:

using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;
using System.IO;
using System.Net.Http;
using UnitTestDemo;

namespace TestDemo
{
    /// <summary>
    /// 基类
    /// </summary>
    public class ApiControllerTestBase
    {
        /// <summary>
        /// 返回HttpClient对象
        /// </summary>
        /// <returns></returns>
        protected HttpClient GetClient()
        {
            var builder = new WebHostBuilder()
                                // 指定使用当前目录
                                .UseContentRoot(Directory.GetCurrentDirectory())
                                // 使用Startup类作为启动类
                                .UseStartup<Startup>()
                                // 设置使用测试环境
                                .UseEnvironment("Testing");
            var server = new TestServer(builder);
            // 创建HttpClient
            HttpClient client = server.CreateClient();

            return client;
        }
    }
}

然后修改StudentTest类,使该类继承自上面创建的基类:

using Newtonsoft.Json;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using UnitTest.Model;
using Xunit;
using Xunit.Abstractions;

namespace TestDemo
{
    public class StudentTest: ApiControllerTestBase
    {
        public HttpClient Client { get; }
        public ITestOutputHelper Output { get; }

        public StudentTest(ITestOutputHelper outputHelper)
        {
            // var server = new TestServer(WebHost.CreateDefaultBuilder()
            //.UseStartup<Startup>());
            // Client = server.CreateClient();

            // 从父类里面获取HttpClient对象
            Client = base.GetClient();
            Output = outputHelper;
        }

        [Fact]
        public async Task Get_ShouldBe_Ok()
        {
            // 2、Act
            var response = await Client.GetAsync($"api/student");

            // Output
            string context = await response.Content.ReadAsStringAsync();
            Output.WriteLine(context);
            List<Student> list = JsonConvert.DeserializeObject<List<Student>>(context);

            // Assert
            Assert.Equal(3, list.Count);
        }

        [Fact]
        public async Task Post_ShouldBe_Ok()
        {
            // 1、Arrange
            Student entity = new Student()
            {
             Name="测试9",
             Age=25,
             Gender="男"
            };

            var str = JsonConvert.SerializeObject(entity);
            HttpContent content = new StringContent(str);

            // 2、Act
            content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json");

            HttpResponseMessage response = await Client.PostAsync("api/student", content);
            string responseBody = await response.Content.ReadAsStringAsync();
            Output.WriteLine(responseBody);

            // 3、Assert
            Assert.Equal("true", responseBody);
        }
    }
}

文章中的示例代码地址:https://github.com/jxl1024/UnitTest

到此这篇关于ASP.NET Core项目使用xUnit进行单元测试的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • xUnit 编写 ASP.NET Core 单元测试的方法

    还记得 .NET Framework 的 ASP.NET WebForm 吗?那个年代如果要在 Web 层做单元测试简直就是灾难啊..NET Core 吸取教训,在设计上考虑到了可测试性,就连 ASP.NET Core 这种 Web 或 API 应用要做单元测试也是很方便的.其中面向接口和依赖注入在这方面起到了非常重要的作用. 本文就来手把手教你如何用 xUnit 对 ASP.NET Core 应用做单元测试..NET Core 常用的测试工具还有 NUnit 和 MSTest,我本人习惯用 x

  • ASP.NET Core中使用xUnit进行单元测试

    单元测试的功能自从MVC的第一个版本诞生的时候,就是作为一个重要的卖点来介绍的,通常在拿MVC与webform比较的时候,单元测试就是必杀底牌,把webform碾压得一无是处. 单元测试的重要性不用多说了,有单元测试的做兜底的项目,好比给开发人员买了份保险,当然这个保险的质量取决于单元测试的质量,那些一路Mock的单元测试,看起来很美,但是什么都cover不到.目前工作中的一个老项目,有2万多个单元测试用例,其中不少是用心之作,真正落实到了业务逻辑,开发人员可以放心的去修改代码,当然一切都必须按

  • ASP.NET Core项目使用xUnit进行单元测试

    目录 一.前言 二.创建示例项目 1.UnitTest.Model 2.UnitTest.Data 3.UnitTest.IRepository 4.UnitTest.Repository 5.UnitTestDemo 6.TestDemo 三.编写单元测试 1.运行单元测试 2.调试单元测试 一.前言 在以前的.NET Framework项目中,我们也写过一些单元测试的项目,而在ASP.NET Core 这种Web或者API应用程序中要做单元测试是很方便的. 这篇文章主要讲解如何使用xUnit

  • ASP.NET Core项目结构教程(4)

    在这一章,我们将讨论 ASP.NET Core项目在文件系统上的组成方式以及不同的文件和目录都是如何协同工作的. 让我们打开在前一章创建的FirstAppDemo项目. 在解决方案资源管理器窗口中,右击解决方案节点并选择"Open Folder in File Explorer". 您将看到在它的根目录下有两个文件︰ FirstAppDemo.sln和global.json. FirstAppDemo.sln文件是一个解决方案文件.Visual Studio多年来在默认情况下一直使用s

  • ASP.NET Core项目配置教程(6)

    在这一章,我们将讨论 ASP.NET Core项目的相关的配置.在解决方案资源管理器中,您将看到 Startup.cs 文件.如果你有以前版本的 ASP.NET的工作经验,你可能希望看到一个 global.asax 文件,您可以在其中编写代码,它是一个编写程序启动时立即执行的代码的文件. 你可能也希望看到一个 web.config 文件,该文件包含您的应用程序执行所需的所有配置参数. 在 ASP.NET Core中,那些文件都没了,取而代之的是 Startup.cs文件. Startup.cs里

  • 在IIS上部署ASP.NET Core项目的图文方法

    概述 与ASP.NET时代不同,ASP.NET Core不再是由IIS工作进程(w3wp.exe)托管,而是使用自托管Web服务器(Kestrel)运行,IIS则是作为反向代理的角色转发请求到Kestrel不同端口的ASP.NET Core程序中,随后就将接收到的请求推送至中间件管道中去,处理完你的请求和相关业务逻辑之后再将HTTP响应数据重新回写到IIS中,最终转达到不同的客户端(浏览器,APP,客户端等).而配置文件和过程都会由些许调整,中间最重要的角色便是AspNetCoreModule,

  • asp.net core项目中如何使用html文件

    前言 大家应该都知道,在asp.net core 项目中,使用html文件一般通过使用中间件来提供服务: 打开 NuGet程序管理控制台 输入install-package Microsoft.aspnetcore.staticfiles 进行添加 ASP.NET Core static files middleware. Includes middleware for serving static files, directory browsing, and default files. 在S

  • IIS部署ASP.NET Core项目及常见问题总结

    部署准备工作 1.服务器开启添加IIS相关功能 1.1. 点击windows搜索到 “启用或关闭windows功能” 1.2 选择添加IIS的部分功能, 如下图所示 2.进入IIS,添加已经发布的网站文件 3.设置应用程序池无托管代码 4.运行你的网站 4.1. 默认运行下, 如出现500.19问题, 则说明网站目录权限不足, 需要给网站添加用户, 设置所有权限. 4.2.打开网站的文件, 添加用户设置权限, 然后刷新网站 5.按照以上的操作, 一般即可正常运行, 如出现不同的异常, 以下总结归

  • ASP.NET Core项目中调用WebService的方法

    一.前言 现实生产中,有一些比较老的系统对外提供的接口都是WebService形式的,如果是使用.NET Framework创建的项目调用WebService非常方便,网上有很多代码示例,这里不在讲解,下面我们讲解如何在ASP.NET Core项目里面调用WebService.首先我们需要创建一个WebService项目和一个ASP.NET Core WebApi项目.创建的WebService代码如下: using System.Web.Services; namespace CoreCall

  • asp.net core项目mvc权限控制:分配权限

    前面的文章介绍了如何进行权限控制,即访问控制器或者方法的时候,要求当前用户必须具备特定的权限,但是如何在程序中进行权限的分配呢?下面就介绍下如何利用Microsoft.AspNetCore.Identity.EntityFrameworkCore框架进行权限分配. 在介绍分配方法之前,我们必须理解权限关系,这里面涉及到三个对象:用户,角色,权限,权限分配到角色,角色再分配到用户,当某个用户属于某个角色后,这个用户就具有了角色所包含的权限列表,比如现在有一个信息管理员角色,这个角色包含了信息删除权

  • 在Asp.net core项目中使用WebSocket

    今天小试了一下在Asp.net core中使用websocket,这里记录一下: 在 Startup 类的 Configure 方法中添加 WebSocket 中间件. app.UseWebSockets(); 它也可以传入一些参数 app.UseWebSockets(new WebSocketOptions() { KeepAliveInterval = TimeSpan.FromSeconds(120), ReceiveBufferSize = 4 * 1024 }); 添加WebSocke

随机推荐