.NET Core系列 :4 测试

2016.6.27 微软已经正式发布了.NET Core 1.0 RTM,可是工具链仍是预览版,一样的大量的开源测试库也都是至少发布了Alpha测试版支持.NET Core, 这篇文章 The State of .Net Core Testing Today 就将各个开源测试库的目前进展进行了汇总。本文咱们的目的是在咱们构建咱们应用程序的时候可以进行测试,如何使用XUnit结合你能够经过为你的项目添加不一样的测试用例NSubstitute进行单元测试,同时对整个项目进行集成测试。此次咱们使用Visual Studio 2015 Update 3进行编写 。xUnit.net是基于.NET Framework 的开源测试工具。经过xUnit.net能够针对C#/F#/VB.NET等进行单元测试。ASP.NET Core 更直接把以往的Visual Studio Unit Test Framework 说再见了,而直接使用上了xUnit.net,xUnit.net基于NUnit 。从网站或者官网上,你能够找到很多xUnit的优势,与NUnit和其余测试框架相比有一下一些优点 
         1)为每一个测试方法产生一个对象实例
         2)取消了[SetUp]和[TearDown]
         3)取消了[ExpectedException]
         4)相似于Aspect的功能
         5)减小了自定义属性(Attribute)的数目
         6)采用泛型
         7)匿名委托
         8)可扩展的断言
         9)可扩展的测试方法
         10)可扩展的测试类html

         了解更多关于xUnit.net能够参考这里(点击打开连接[舍弃Nunit拥抱Xunit])。git

 

使用xUnit.net 单元测试

 

 

 

首先咱们相似于.NET Core系列 :3 、使用多个项目 建立一个解决方案testdemo,添加一个类库项目叫作DotnetCoreLib,Library.cs 也替换为:github

namespace DotnetCoreLib
{
    public class Calculator
    {
        public int Multi(int x, int y)
        {
            return x * y;
        }
    }数据库

}json

image

下面咱们要建立一个针对DotnetCoreLib的测试项目,具体建立过程咱们参照文章 https://github.com/dotnet/core-docs/tree/master/samples/core/getting-started/unit-testing-using-dotnet-test ,咱们修改DotnetCoreLibTest 项目的project.json ,增长XUnit相关的nuget包引用,并修改部分配置。api

 

image 

还有咱们设置Framework节点为 netcoreapp1.0, 依赖的xunit 和xunit.runner的包服务器

"dependencies": {
    "dotnet-test-xunit": "2.2.0-preview2-build1029",
    "DotnetCoreLib": {
      "version": "1.0.0-*",
      "target": "project"
    },
    "xunit": "2.2.0-beta2-build3300",
    "xunit.runner.console": "2.2.0-beta2-build3300"
  }网络

 

Calculator接下来就开始测试咱们的类库Calculator, 修改Class1.cs为CalculatorTest.cs ,app

 

using DotnetCoreLib;
using Xunit;框架

 

namespace DotnetCoreLibTest
{
    public class CalTest
    {
        private readonly Calculator calculator;


        public CalTest()
        {
            calculator = new Calculator();
        }

 

        [Fact]
        public void OneMutiOneIsOne()
        {
            var result = calculator.Multi(1, 1);
            Assert.Equal(1, result);
        }

 

        [Theory]
        [InlineData(-1)]
        [InlineData(0)]
        [InlineData(1)]
        public void ReturnValue(int value)
        {
            var result = calculator.Multi(1,value);

            Assert.Equal(result, value);
        }
    }
}

 

上面的两个测试,咱们分别用了2个特性[Fact] 和[Theory], [Fact]属性表示为一个方法的单个测试,[Theory]属性表示执行相同的代码,可是有不一样的输入的参数的测试套件。[InlineData] 属性可用于指定为这些输入值。经过特性[Fact] 和[Theory],xUnit就理解了这是个测试方法,而后运行这个方法。在一个测试方法中,咱们通常遵循包含三步骤的AAA模式:

  1. Arrange:为测试准备
  2. Act:运行SUT(实际测试的代码)
  3. Assert:校验结果

下面咱们运行dotnet test 就能够看到结果了。

C:\Users\geffz\Documents\Visual Studio 2015\Projects\TestDemo\DotnetCoreLibTest>dotnet test
Project DotnetCoreLib (.NETCoreApp,Version=v1.0) was previously compiled. Skipping compilation.
Project DotnetCoreLibTest (.NETCoreApp,Version=v1.0) was previously compiled. Skipping compilation.
xUnit.net .NET CLI test runner (64-bit .NET Core win10-x64)
  Discovering: DotnetCoreLibTest
  Discovered:  DotnetCoreLibTest
  Starting:    DotnetCoreLibTest
  Finished:    DotnetCoreLibTest
=== TEST EXECUTION SUMMARY ===
   DotnetCoreLibTest  Total: 4, Errors: 0, Failed: 0, Skipped: 0, Time: 0.206s
SUMMARY: Total: 1 targets, Passed: 1, Failed: 0.

 

上面的输出咱们知道已经执行了4个测试,都经过了,[Face]特性标识表示固定输入的测试用例,而[Theory]特性标识表示能够指定多个输入的测试用例,结合InlineData特性标识使用。在上面的例子里,总共使用了三次InlineData特性标识,每次设定的值都不一样,在执行单元测试时,设定的值会被测试框架赋值到对应的测试方法的参数里。你能够经过为你的项目添加不一样的测试用例,这样就可让你的代码获得充分测试。

 

xUnit.net 搭配NSubstitute 进行单元测试

 

   在一个分层结构清晰的项目里,各层之间依赖于事先约定好的接口。在多人协做开发时,大多数人都只会负责本身的那一部分模块功能,开发进度一般状况下也不一致。当某个开发人员须要对本身的模块进行单元测试而依赖的其余模块尚未开发完成时,则须要对依赖的接口经过Mock的方式提供模拟功能,从而达到在不实际依赖其余模块的具体功能的状况下完成本身模块的单元测试工做。这时咱们一般须要有一个单元测试模拟类库,一直以来,开发者对 mocking 类库的语法的简洁性有强烈的需求,NSubstitute 试图知足这一需求。简单明了的语法可让咱们将重心放在测试自己,而不是纠缠在测试替代实例的建立和配置上。NSubstitute 已尝试将最经常使用的操做需求简单化、易用化,并支持一些不经常使用的或探索性的功能,与此同时还尽量地将其语法向天然语言靠近。关于NSubstitute的更详细信息请往 NSubstitute彻底手册索引

 

NSubstitute 已经发布2.0 RC版本支持.NET Core。引入NSubstitute 相关nuget包:

image

咱们把Calculator 类重构下提取出接口ICalculator:

    public interface ICalculator
    {
        int Multi(int x, int y);
    }

 

咱们可让NSubstitute来建立类型实例的替代实例,能够建立诸如 Stub、Mock、Fake、Spy、Test Double 等,但当咱们只是想要一个能有必定程度控制的替代实例时,为何咱们要困扰于此呢?咱们能够告诉被建立的替代实例,当方法被调用时返回一个值:

     [Fact]
      public void Test_GetStarted_ReturnSpecifiedValue()
      {
          ICalculator calculator = Substitute.For<ICalculator>();
          calculator.Multi(1, 2).Returns(2);

          int actual = calculator.Multi(1, 2);
          Assert.Equal(2, actual);
      }

下面咱们运行dotnet test 就能够看到结果了,增长了上面的2个用例,关于NSubstitute的更详细信息请往 NSubstitute彻底手册索引

image

 

集成测试

上面咱们只是对逻辑进行了单元测试。对于Asp.Net Core项目,还须要模拟在网站部署的状况下对各个请求入口进行测试。NET Core 可为快速轻松集成测试提供很是棒的支持。

TestServer 类为 ASP.NET Core 中的集成测试执行大部分繁重操做,Microsoft.AspNetCore.TestHost 包中具备此类。本节内容来自于MSDN杂志《 ASP.NET Core - 实际的 ASP.NET Core MVC 筛选器》,这些集成测试不须要数据库或 Internet 链接或运行的 Web 服务器。它们如同单元测试同样快速简单,但最重要的是,它们容许你在整个请求管道中测试 ASP.NET 应用,而不仅是控制器类中的孤立方法。建议尽量编写单元测试,并针对没法单元测试的行为退回到集成测试,但使用此类高性能方式在 ASP.NET Core 中运行集成测试是很是棒的。

 

经过在一个工程里同时模拟了服务端(TestServer)和客户端(HttpClient)的通讯,从而达到了总体测试WebApi接口的目的,相关的代码放在https://github.com/ardalis/GettingStartedWithFilters/tree/master/IntegrationTests 。文章对ASP.NET CORE MVC的筛选器进行测试,因为很难经过编写单元测试来测试此类场景,可是能够经过ASP.NET Core 的集成测试来达到相同的目的。

using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using Filters101;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.TestHost;

 

namespace IntegrationTests
{
    public class AuthorsControllerTestBase
    {
        protected HttpClient GetClient()
        {
            var builder = new WebHostBuilder()
                .UseContentRoot(Directory.GetCurrentDirectory())
                .UseStartup<Startup>()
                .UseEnvironment("Testing");
            var server = new TestServer(builder);
            var client = server.CreateClient();

            // client always expects json results
            client.DefaultRequestHeaders.Clear();
            client.DefaultRequestHeaders.Accept.Add(
                new MediaTypeWithQualityHeaderValue("application/json"));

            return client;
        }
    }
}

 

 

using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Filters101.Models;
using Newtonsoft.Json;
using Xunit;

namespace IntegrationTests.AuthorsController
{
    public class Get : AuthorsControllerTestBase
    {
        private readonly HttpClient _client;

        public Get()
        {
            _client = base.GetClient();
        }

        [Theory]
        [InlineData("authors")]
        [InlineData("authors2")]
        public async Task ReturnsListOfAuthors(string controllerName)
        {
            var response = await _client.GetAsync($"/api/{controllerName}");
            response.EnsureSuccessStatusCode();
            var stringResponse = await response.Content.ReadAsStringAsync();
            var result = JsonConvert.DeserializeObject<IEnumerable<Author>>(stringResponse).ToList();

            Assert.Equal(2, result.Count());
            Assert.Equal(1, result.Count(a => a.FullName == "Steve Smith"));
            Assert.Equal(1, result.Count(a => a.FullName == "Neil Gaiman"));
        }
    }
}

此案例中的客户端是标准的 System.Net.Http.HttpClient,你可使用它向服务器发出请求,正如同经过网络同样。但由于全部请求都在内存中进行,因此测试极其快速可靠。在cmd窗口执行单元测试,查看测试结果

image

相关文章
相关标签/搜索