延續 Day09 實作官網 ASP.NET Core 教學我們把專案加入 xUnit Test
測試架構為 xUnit。 物件模擬架構為 Moq。
在專案目錄下加入 tests 資料夾,增加 xUnit 專案
dotnet new xunit -o Tests
安裝套件
dotnet add package Microsoft.EntityFrameworkCore.InMemory
dotnet add package Microsoft.AspNetCore.TestHost
dotnet add package Moq
dotnet add package Newtonsoft.Json
dotnet add package System.Diagnostics.TraceSource
dotnet add package System.Net.Http
增加參考
dotnet add ./Tests.csproj reference ../PellokITHome.csproj
創建 Utilities
mkdir Utilities
touch Utilities/Utilities.cs
Utilities.cs的內容如下:
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using PellokITHome.Data;
namespace Tests
{
public static class Utilities
{
#region snippet1
public static DbContextOptions<PellokITHomeContext> TestDbContextOptions()
{
// Create a new service provider to create a new in-memory database.
var serviceProvider = new ServiceCollection()
.AddEntityFrameworkInMemoryDatabase()
.BuildServiceProvider();
// Create a new options instance using an in-memory database and
// IServiceProvider that the context should resolve all of its
// services from.
var builder = new DbContextOptionsBuilder<PellokITHomeContext>()
.UseInMemoryDatabase("InMemoryDb")
.UseInternalServiceProvider(serviceProvider);
return builder.Options;
}
#endregion
}
}
創建資料測試
mkdir UnitTests
touch UnitTests/DataAccessLayerTest.cs
內容如下:
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Xunit;
using PellokITHome.Data;
using PellokITHome.Models;
namespace Tests.UnitTests
{
public class DataAccessLayerTest
{
[Fact]
public async Task GetMessagesAsync_MessagesAreReturned()
{
using (var db = new PellokITHomeContext(Utilities.TestDbContextOptions()))
{
// Arrange
var expectedMessages = PellokITHomeContext.GetSeedingArticles();
await db.AddRangeAsync(expectedMessages);
await db.SaveChangesAsync();
// Act
var result = await db.GetArticlesAsync();
// Assert
var actualMessages = Assert.IsAssignableFrom<List<Article>>(result);
Assert.Equal(
expectedMessages.OrderBy(m => m.ID).Select(m => m.Title),
actualMessages.OrderBy(m => m.ID).Select(m => m.Title));
}
}
[Fact]
public async Task AddMessageAsync_MessageIsAdded()
{
using (var db = new PellokITHomeContext(Utilities.TestDbContextOptions()))
{
// Arrange
var recId = 10;
var expectedMessage = new Article() { ID = recId, Title = "Message" };
// Act
await db.AddArticleAsync(expectedMessage);
// Assert
var actualMessage = await db.FindAsync<Article>(recId);
Assert.Equal(expectedMessage, actualMessage);
}
}
[Fact]
public async Task DeleteAllMessagesAsync_MessagesAreDeleted()
{
using (var db = new PellokITHomeContext(Utilities.TestDbContextOptions()))
{
// Arrange
var seedMessages = PellokITHomeContext.GetSeedingArticles();
await db.AddRangeAsync(seedMessages);
await db.SaveChangesAsync();
// Act
await db.DeleteAllArticlesAsync();
// Assert
Assert.Empty(await db.Articles.AsNoTracking().ToListAsync());
}
}
[Fact]
public async Task DeleteMessageAsync_MessageIsDeleted_WhenMessageIsFound()
{
using (var db = new PellokITHomeContext(Utilities.TestDbContextOptions()))
{
#region snippet1
// Arrange
var seedMessages = PellokITHomeContext.GetSeedingArticles();
await db.AddRangeAsync(seedMessages);
await db.SaveChangesAsync();
var recId = 1;
var expectedMessages =
seedMessages.Where(message => message.ID != recId).ToList();
#endregion
#region snippet2
// Act
await db.DeleteArticleAsync(recId);
#endregion
#region snippet3
// Assert
var actualMessages = await db.Articles.AsNoTracking().ToListAsync();
Assert.Equal(
expectedMessages.OrderBy(m => m.ID).Select(m => m.Title),
actualMessages.OrderBy(m => m.ID).Select(m => m.Title));
#endregion
}
}
#region snippet4
[Fact]
public async Task DeleteMessageAsync_NoMessageIsDeleted_WhenMessageIsNotFound()
{
using (var db = new PellokITHomeContext(Utilities.TestDbContextOptions()))
{
// Arrange
var expectedMessages = PellokITHomeContext.GetSeedingArticles();
await db.AddRangeAsync(expectedMessages);
await db.SaveChangesAsync();
var recId = 4;
// Act
try
{
await db.DeleteArticleAsync(recId);
}
catch
{
// recId doesn't exist
}
// Assert
var actualMessages = await db.Articles.AsNoTracking().ToListAsync();
Assert.Equal(
expectedMessages.OrderBy(m => m.ID).Select(m => m.Title),
actualMessages.OrderBy(m => m.ID).Select(m => m.Title));
}
}
#endregion
}
}
修改PellokITHomeContext.cs檔案
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using PellokITHome.Models;
namespace PellokITHome.Data
{
public class PellokITHomeContext : DbContext
{
public PellokITHomeContext (
DbContextOptions<PellokITHomeContext> options)
: base(options)
{
}
public DbSet<PellokITHome.Models.Article> Articles { get; set; }
#region snippet1
public async virtual Task<List<Article>> GetArticlesAsync()
{
return await Articles
.OrderBy(Article => Article.Title)
.AsNoTracking()
.ToListAsync();
}
#endregion
#region snippet2
public async virtual Task AddArticleAsync(Article Article)
{
await Articles.AddAsync(Article);
await SaveChangesAsync();
}
#endregion
#region snippet3
public async virtual Task DeleteAllArticlesAsync()
{
foreach (Article Article in Articles)
{
Articles.Remove(Article);
}
await SaveChangesAsync();
}
#endregion
#region snippet4
public async virtual Task DeleteArticleAsync(int id)
{
var Article = await Articles.FindAsync(id);
if (Article != null)
{
Articles.Remove(Article);
await SaveChangesAsync();
}
}
#endregion
public void Initialize()
{
Articles.AddRange(GetSeedingArticles());
SaveChanges();
}
public static List<Article> GetSeedingArticles()
{
return new List<Article>()
{
new Article(){ Title = "Day01 Azure 的自我修煉" },
new Article(){ Title = "Day02 申請Azure帳號" },
new Article(){ Title = "Day03 Resource Group 資源群組" }
};
}
}
}
修改以上內容後,需要重建DB,確認專案沒有問題,執行正常。
dotnet test
合併兩個專案為一個解決方案
# 創建方案
dotnet new sln -o PellokITHomePipeline
# 到方案目錄下
cd PellokITHomePipeline
# 複製專案
cp -r ../PellokITHome .
# 移除專案暫存檔
rm -rf PellokITHome/.git
rm -rf PellokITHome/.vscode
rm -rf PellokITHome/obj
rm -rf PellokITHome/bin
# 複製專案
cp -r ../Tests .
# 移除專案暫存檔
rm -rf Tests/.git
rm -rf Tests/.vscode
rm -rf Tests/obj
rm -rf Tests/bin
# 加入專案到方案
dotnet sln add ./PellokITHome/PellokITHome.csproj
dotnet sln add ./Tests/Tests.csproj
新增 .gitignore
*/bin/*
*/obj/*
*/.vscode/*
*.db
提交索引、提交版本
mv PellokITHome/.gitignore .
git init
git status
git commit -m "first commit"
code -r .
# 測試
dotnet test
上一篇 Day20 實作 Dotnet Test 測試範例
下一篇 Day22 整合CI測試到 Azure Pipeline 服務