单元测试(二)基本使用争议篇

介绍

常用的单元测试是测试方法、API等,下面我们来演示一下Xunit测试框架的简单使用,有些是为了演示而写的单元测试。

最下面有反转,一定要看到最后

操作

创建单元测试项目

本次文章还在原来项目的基础上进行操作,右键解决方案添加单元测试项目

12a0ce74e4f91d8dfa2218bf6091f0ff.png
img

选择框架版本为.Net 5.0

1ecab4c833aede63bf7275fd7ab55ed7.png
img

单元测试项目创建完成。然后引用我们的包

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>.NETCoreApp,Version=v5.0</TargetFramework>

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

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.TestHost" Version="5.0.10" />
    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.4" />
    <PackageReference Include="xunit" Version="2.4.1" />
    <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
    <PackageReference Include="coverlet.collector" Version="3.0.2" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\Net5ByDocker\Net5ByDocker.csproj" />
  </ItemGroup>

</Project>

TargetFramework:指定测试项目的目标框架

IsPackable:设置是否允许打包单元测试项目

xunit:该xunit包引入了三个子包,其中包括大多数开发人员想要的功能:(xunit.core测试框架本身)、 xunit.assert(包含Assert类的库)和xunit.analyzers(使 Roslyn 分析器能够检测单元测试和 xUnit.net 可扩展性的常见问题)

包xunit.runner.visualstudio和Microsoft.NET.Test.Sdk 是能够在 Visual Studio 中运行测试项目以及使用 dotnet test.

coverlet.collector:该coverlet.collector包允许收集代码覆盖率。如果您不打算收集代码覆盖率,则应删除此包引用。

如果想使用MSTest框架,那么只需要在需要进行测试的方法上面点击右键,创建单元测试

383e1ec73909cf3b0fe80a2b3758d9ac.png
img

点击确定后将会为我们创建一个单元测试的应用程序,关于User控制器的单元测试我们就写在这个里面

b9199f2f4b91786af6468e9b71a62dd9.png
img

这里我们并不使用MSTest进行测试。

创建基类文件BaseTest

public class BaseTest
{
    protected readonly ITestOutputHelper Output;
    public BaseTest(ITestOutputHelper output)
    {
        Output = output;
    }

    public string SerializeObject(object obj)
    {
        JsonSerializerSettings settings = new JsonSerializerSettings
        {
            Formatting = Formatting.Indented,
            ContractResolver = new CamelCasePropertyNamesContractResolver()
        };
        return JsonConvert.SerializeObject(obj, settings);
    }
}

/// <summary>
/// 构建webhost
/// </summary>
public class BaseWebHostTest : BaseTest
{
    protected readonly TestServer Server;
    public BaseWebHostTest(ITestOutputHelper helper) : base(helper)
    {
        var service = Host.CreateDefaultBuilder()
                            .ConfigureWebHostDefaults(webBuilder =>
                            {
                                webBuilder.UseStartup<Startup>();
                            }).Build().Services;
        Server = new TestServer(service);
    }
}

/// <summary>
/// 只是测试控制器Api
/// </summary>
public class BaseHostTest : BaseTest
{
    protected readonly TestServer Server;
    public BaseHostTest(ITestOutputHelper helper) : base(helper)
    {
        Server = new TestServer(WebHost.CreateDefaultBuilder()
                .UseEnvironment("Development")//测试使用
                .UseStartup<Startup>());
    }
}

测试API

就以演示获取用户信息为例,我们测试调用接口后是否返回状态码为200

编写构造函数并赋值HttpClient

public UserControllerTests(ITestOutputHelper helper) : base(helper)
{
    Client = Server.CreateClient();

    //var token = ""; // 可以对HttpClient进行一些自定请求头等操作
    //Client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
}

public HttpClient Client { get; }

测试

[Fact()]
public async Task Get_User_ReturnOk()
{
    //Arrange  赋值区域
            
    //Act
    var response = await Client.GetAsync("api/user/getlist");

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

    Output.WriteLine(await response.Content.ReadAsStringAsync());
}

写单元测试一般有三个步骤:Arrange,Act 和 Assert。

  • Arrange 是准备阶段,这个阶段是准备工作,比如模拟数据、初始化对象等;

  • Act 是行为阶段,这个阶段是用准备好的数据去调用要测试的方法;

  • Assert 是断定阶段,就是把调用目标方法返回的值和预期的值进行比较,如果和预期一致说明测试通过,否则为失败。

点击方法名字右键运行测试或者调试测试

6e657f87ce8e2c9f7e7c164f8ceb707b.png
img

测试方法

比如我们去对IUserService里面的GetListAsync做单元测试,继承自公共类,通过依赖注入获取IUserService服务

public class UserServiceTest : BaseWebHostTest
{
    private readonly IUserService _userService;

    public UserServiceTest(ITestOutputHelper helper) : base(helper)
    {
        _userService = Server.Services.GetRequiredService<IUserService>();
    }

    [Fact]
    public async Task GetUser_ReturnOk()
    {
        //Arrange:准备阶段 

        //Act:行为阶段
        var response = await _userService.GetListAsync();

        //Assert:断言阶段
        Assert.NotNull(response);
        Output.WriteLine(JsonConvert.SerializeObject(response));
    }
}

点击方法名字右键运行测试或调试测试

1d9cfdb06e65e8b1ec45995e371b43e8.png
img

单元测试成功

并行运行测试

在Xunit的2.x版本以后支持并行运行测试。这样子相比如之前可以更好利用服务器性能。

新建测试类RunnerTimeTest,

public class RunnerTimeTest
{
    [Fact]
    public void Test1()
    {
        Thread.Sleep(2000);
    }

    [Fact]
    public void Test2()
    {
        Thread.Sleep(3000);
    }
}

我们猜一下运行该测试类的话需要耗时多少?2s?3s?

9f3066b3ba23725f2c339dc1fe015aaa.png
img

通过这个结果我们可以得出来一个测试类内并不是并行执行的。默认情况下每一个测试类都是一个唯一的测试集合,同一个测试类的测试不会彼此并行运行。那么我们将这两个测试方法分别放入不同的测试类中

public class RunnerTimeTest
{
    [Fact]
    public void Test1()
    {
        Thread.Sleep(2000);
    }
}

public class RunnerTimeTest2
{
    [Fact]
    public void Test2()
    {
        Thread.Sleep(3000);
    }
}

运行查看效果

72ad4e4110c18b52821a5b10e2556e48.png
img

可以得到不同的测试类之间是并行执行的。

批评

这个是我理解的单元测试样子,但是经过同事的严厉批评,我知道了应该使用单一变量原则,控制一处不同,其他变量保持相同,而不是像该文章一样,依赖项不可控。

资料

命令行创建单元测试项目:https://xunit.net/docs/getting-started/netcore/cmdline

ASP.NET Core单元测试:https://www.cnblogs.com/willick/p/aspnetcore-unit-tests-with-xunit.html

并行运行测试:https://xunit.net/docs/running-tests-in-parallel

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值