第一部分、XUnit修改 Project.json 文件內(nèi)容,增加XUnit相關(guān)的nuget包引用,并修改部分配置。 1 { 2 "version": "1.0.0-*", 3 "testRunner": "xunit", // 設(shè)置測試工具為xunit 4 5 "buildOptions": { 6 "debugType": "portable", 7 "emitEntryPoint": true 8 }, 9 "dependencies": { 10 "Microsoft.NETCore.App": { 11 "type": "platform", 12 "version": "1.0.0" 13 }, 14 "Microsoft.AspNetCore.Server.Kestrel": "1.0.0", 15 "Microsoft.AspNetCore.Mvc": "1.0.0", 16 "Microsoft.Extensions.Logging": "1.0.0", 17 "Microsoft.Extensions.Logging.Console": "1.0.0", 18 "Microsoft.Extensions.Logging.Debug": "1.0.0", 19 "Microsoft.Extensions.Logging.Filter": "1.0.0", 20 "NLog.Extensions.Logging": "1.0.0-rtm-alpha2", 21 "Autofac.Extensions.DependencyInjection": "4.0.0-rc3-309", 22 "Microsoft.Extensions.Configuration": "1.0.0", 23 "Microsoft.Extensions.Configuration.FileExtensions": "1.0.0", 24 "Microsoft.Extensions.Configuration.Json": "1.0.0", 25 "Microsoft.Extensions.Options.ConfigurationExtensions": "1.0.0", 26 "xunit": "2.2.0-beta2-build3300", 27 "dotnet-test-xunit": "2.2.0-preview2-build1029" 28 }, 29 "frameworks": { 30 "netcoreapp1.0": { 31 // 設(shè)置兼容框架 32 "imports": [ 33 "dotnet54", 34 "portable-net45+win8" 35 ] 36 } 37 } 38 } 增加一個Demo類和一個測試類 1 namespace WebApiFrame 2 { 3 public class DemoModel 4 { 5 public int Add(int a, int b) 6 { 7 return a + b; 8 } 9 10 public bool IsOdd(int num) 11 { 12 return num % 2 == 1; 13 } 14 } 15 } 1 using Xunit; 2 3 namespace WebApiFrame.Test 4 { 5 public class DemoModelTest 6 { 7 private readonly DemoModel _demo; 8 9 public DemoModelTest() 10 { 11 _demo = new DemoModel(); 12 } 13 14 [Fact] 15 public void AddTest() 16 { 17 int result = _demo.Add(1, 2); 18 Assert.Equal(3, result); 19 } 20 } 21 } 打開cmd窗口,進入到項目根目錄,輸入命令 dotnet test ,將啟動單元測試,可以在輸出查看測試結(jié)果 再對另外一個方法添加單元測試代碼 1 [Theory] 2 [InlineData(1)] 3 [InlineData(2)] 4 [InlineData(3)] 5 public void IsOdd(int num) 6 { 7 bool result = _demo.IsOdd(num); 8 Assert.True(result, $"{num} is not odd."); 9 } 再次啟動單元測試,查看測試結(jié)果 結(jié)果顯示執(zhí)行了四個單元測試用例,有一個失敗了。 通過比較上面兩個測試方法可以發(fā)現(xiàn)使用的特性標識不同,測試方法的參數(shù)列表也不相同。 [Face]特性標識表示固定輸入的測試用例,而[Theory]特性標識表示可以指定多個輸入的測試用例,結(jié)合InlineData特性標識使用。在上面的例子里,總共使用了三次InlineData特性標識,每次設(shè)定的值都不同,在執(zhí)行單元測試時,設(shè)定的值會被測試框架賦值到對應(yīng)的測試方法的參數(shù)里。
第二部分、Moq在之前的例子里已經(jīng)定義了如下接口和類
IUserRepository.cs
UsersController.cs
我們要對 UsersController.cs 的方法進行單元測試,同時UserRepository實例是通過構(gòu)造函數(shù)依賴注入的,所以要借助Moq來模擬這個實例的生成。 在引入Moq包之前,先要修改NuGet.Config配置文件,增加package包源地址。 NuGet.Config配置文件路徑: C:\Users\{user}\AppData\Roaming\NuGet 1 <?xml version="1.0" encoding="utf-8"?> 2 <configuration> 3 <activePackageSource> 4 <add key="" value="https://www./api/v2/" /> 5 </activePackageSource> 6 <packageSources> 7 <add key="" value="https://api./v3/index.json" protocolVersion="3" /> 8 9 <!-- 增加的程序包源地址 --> 10 <add key="aspnet-contrib" value="https://www./F/aspnet-contrib/api/v3/index.json" /> 11 </packageSources> 12 </configuration> 引入Moq相關(guān)nuget包: "moq.netcore": "4.4.0-beta8" 添加單元測試類 1 using System.Collections.Generic; 2 using System.Linq; 3 using Microsoft.AspNetCore.Mvc; 4 using Moq; 5 using WebApiFrame.Controllers; 6 using WebApiFrame.Models; 7 using WebApiFrame.Repositories; 8 using Xunit; 9 10 namespace WebApiFrame.Test 11 { 12 public class UsersControllerTest 13 { 14 private readonly UsersController _controller; 15 16 public UsersControllerTest() 17 { 18 var mockRepo = new Mock<IUserRepository>(); 19 mockRepo.Setup(repo => repo.GetAll()).Returns(GetUsers()); 20 _controller = new UsersController(mockRepo.Object); 21 } 22 23 [Fact] 24 public void GetAllTest() 25 { 26 IActionResult actionResult = _controller.GetAll(); 27 var objectResult = Assert.IsType<ObjectResult>(actionResult); 28 var result = Assert.IsAssignableFrom<IEnumerable<User>>(objectResult.Value); 29 Assert.Equal(3, result.Count()); 30 } 31 32 private IEnumerable<User> GetUsers() 33 { 34 return new List<User>() 35 { 36 new User(){ Id = 1, Name = "name:1", Sex = "Male" }, 37 new User(){ Id = 2, Name = "name:2", Sex = "Female" }, 38 new User(){ Id = 3, Name = "name:3", Sex = "Male" }, 39 }; 40 } 41 } 42 } 在cmd窗口執(zhí)行單元測試,查看測試結(jié)果 在一個分層結(jié)構(gòu)清晰的項目里,各層之間依賴于事先約定好的接口。在多人協(xié)作開發(fā)時,大多數(shù)人都只會負責(zé)自己的那一部分模塊功能,開發(fā)進度通常情況下也不一致。當(dāng)某個開發(fā)人員需要對自己的模塊進行單元測試而依賴的其他模塊還沒有開發(fā)完成時,則需要對依賴的接口通過Mock的方式提供模擬功能,從而達到在不實際依賴其他模塊的具體功能的情況下完成自己模塊的單元測試工作。
第三部分、集成測試以上的例子只是對邏輯進行了單元測試。對于Asp.Net Core項目,還需要模擬在網(wǎng)站部署的情況下對各個請求入口進行測試。通常情況下可以借助Fiddler等工具完成,在.Net Core里也可以用編程的方式完成測試。 首先引入測試需要的nuget包。因為我們測試的是WebApi接口,響應(yīng)內(nèi)容都是json格式的字符串,所以還需要引用json序列化的nuget包。 "Microsoft.AspNetCore.TestHost": "1.0.0", "Newtonsoft.Json": "9.0.1" 添加測試類 1 using System.Collections.Generic; 2 using System.Net.Http; 3 using System.Threading.Tasks; 4 using Microsoft.AspNetCore.Hosting; 5 using Microsoft.AspNetCore.TestHost; 6 using Newtonsoft.Json; 7 using WebApiFrame.Models; 8 using Xunit; 9 10 namespace WebApiFrame.Test 11 { 12 public class WebApiFrameTest 13 { 14 private readonly TestServer _server; 15 private readonly HttpClient _client; 16 17 public WebApiFrameTest() 18 { 19 _server = new TestServer(new WebHostBuilder().UseStartup<Startup>()); 20 _client = _server.CreateClient(); 21 } 22 23 [Fact] 24 public async Task GetAllTest() 25 { 26 var response = await _client.GetAsync("/api/users"); 27 response.EnsureSuccessStatusCode(); 28 29 var responseString = await response.Content.ReadAsStringAsync(); 30 IList<User> users = JsonConvert.DeserializeObject<IList<User>>(responseString); 31 32 Assert.Equal(3, users.Count); 33 } 34 35 [Theory] 36 [InlineData(1)] 37 [InlineData(2)] 38 [InlineData(3)] 39 public async Task GetTest(int id) 40 { 41 var response = await _client.GetAsync($"/api/users/{id}"); 42 response.EnsureSuccessStatusCode(); 43 44 var responseString = await response.Content.ReadAsStringAsync(); 45 User user = JsonConvert.DeserializeObject<User>(responseString); 46 47 Assert.NotNull(user); 48 } 49 } 50 } 在cmd窗口執(zhí)行單元測試,查看測試結(jié)果 在上面的例子里,通過在一個工程里同時模擬了服務(wù)端(TestServer)和客戶端(HttpClient)的通信,從而達到了整體測試WebApi接口的目的。 |
|