NUnit

前情提要

以下範例是 .NET Core 6.0.0 的範例

需要額外下載套件

Moq (用來做 mockito 的相關行為)

FluentAssertions (用來提升可讀性)

單元測試

硬斷言

[Test]
public void Hard_Assertion()
{
    // Arrange
    string str = "123";
    string expected = "2";

    // Act
    string actual = str.Substring(1, 1);

    // Assert
    // 原生寫法
    Assert.AreEqual(actual, expected);
    // 使用 FluentAssertions 提升可讀性
    actual.Should().Be(expected);
}

軟斷言

[Test]
public void Soft_Assertion()
{
    // Arrange
    string str = "123";

    // Act
    string actual1 = str.Substring(1, 1);
    string actual2 = str.Substring(1, 2);

    // Assert
    // 原生寫法
    Assert.Multiple(() =>
    {
        Assert.AreEqual("2",actual1);
        Assert.AreEqual("23",actual2);
    });
    // 使用 FluentAssertions 提升可讀性
    using (new AssertionScope())
    {
        actual1.Should().Be("2");
        actual2.Should().Be("23");
    }
}

驗證 Exception

[Test]
public void Throwing_Exception()
{
    // Arrange

    // 原生寫法
    // Act
    var ex = Assert.Throws<Exception>(()=>throwException());
    // Assert
    Assert.That(ex.Message, Does.Contain("Magic"));

    // 使用 FluentAssertions 提升可讀性
    // Act
    Action actual = () => throwException();
    // Assert
    actual.Should().Throw<Exception>().WithMessage("*Magic*");
}

private void throwException()
{
    throw new Exception("Magic");
}

API 測試

參考資料:(https://www.ruyut.com/2023/04/nunit-aspnet-core6-api-integration-testing.html)

*還有重構空間,此處僅是API測試的範例,可讀性方面請參考單元測試部分

using System.Text;
using Microsoft.AspNetCore.Mvc.Testing;
using Newtonsoft.Json;
using JsonSerializer = System.Text.Json.JsonSerializer;

namespace {namespace};

public class {controllerName}Test
{
    private WebApplicationFactory<Program> _factory = null!;
    private HttpClient _client = null!;

    [OneTimeSetUp]
    public void Init()
    {
        _factory = new WebApplicationFactory<Program>();
        _client = _factory.CreateClient();
        // 這行會切正式環境,拿掉就是測試環境 Debugger
        // Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT","Production"); //正式環境
        Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Development"); //測試環境
    }

    [OneTimeTearDown]
    public void Cleanup()
    {
        _factory.Dispose();
    }

    [Test]
    public async Task {MethodName}_{type}()
    {
        // Arrange
        var type = "keyWord";
        string id= "id";
        var url = $"url{type}?id={id}";

        // Act
        var response = await WhenGet<JsonResultBase>(url);

        // Assert
        ShouldBeSuccessHasData(response);
        {MethodName}ResponseShouldBe(response!.data);
    }
    
    [Test]
    public async Task {MethodName}_{type}()
    {
        // Arrange
        var type = "keyword2";
        string id= "id";
        string day = "1";
        var url = $"url{type}?id={id}&day={day}";

        // Act
        var response = await WhenGet<JsonResultBase>(url);

        // Assert
        ShouldBeSuccessHasData(response);
        {MethodName}ResponseShouldBe(response!.data);
    }
    
    [Test]
    public async Task {MethodName}_{type}_shouldBeNull()
    {
        // Arrange
        var type = "type";
        string id= "id";
        var url = $"url{type}?id={id}&day={day}";

        // Act
        var response = await WhenGet<JsonResultBase>(url);

        // Assert
        ShouldBeSuccessHasData(response);
        {MethodName}ResponseShouldBeNull(response!.data);
    }

    private async Task<T?> WhenGet<T>(string url)
    {
        var response = await _client.GetAsync(url);
        var content = await response.Content.ReadAsStringAsync();
        //Console.Out.WriteLine(content);
        T? responseBase = JsonConvert.DeserializeObject<T>(content);
        return responseBase;
    }

    private static void ShouldBeSuccessHasData(JsonResultBase? responseBase)
    {
        Assert.IsTrue(responseBase.success);
        Assert.IsNull(responseBase.message);
        Assert.IsNotNull(responseBase.data);
    }
    
    private static void ShouldBeSuccessHasNoData(JsonResultBase? responseBase)
    {
        Assert.IsTrue(responseBase.success);
        Assert.IsNull(responseBase.message);
        Assert.IsNull(responseBase.data);
    }

    private static void {MethodName}ResponseShouldBe(object obj)
    {
        {responseClass}? data =
            JsonConvert.DeserializeObject<{responseClass}>(JsonConvert.SerializeObject(obj));
        Assert.IsNotNull(data.Content);
        Assert.IsNotNull(data.ContentFileInfo);
        Assert.IsNotNull(data.ContentFileInfo);
        Assert.IsNotNull(data.ContentFileInfo[0].type);
        Assert.IsNotNull(data.ContentFileInfo[0].files);
        Assert.IsNotNull(data.ContentFileInfo[0].files[0].id);
        Assert.IsNotNull(data.ContentFileInfo[0].files[0].fileName);
        Assert.IsNotNull(data.ContentFileInfo[0].files[0].base64);
    }
    
    private static void {MethodName}ResponseShouldBeNull(object obj)
    {
        {responseClass}? data =
            JsonConvert.DeserializeObject<{responseClass}>(JsonConvert.SerializeObject(obj));
        Assert.IsNotNull(data);
        Assert.IsNull(data.Content);
        Assert.IsNull(data.ContentFileInfo);
    }

    [Test]
    public async Task {MethodName}_TravelNotes()
    {
        // Arrange
        var url = $"url";
        var user = Given{MethodName}Param("TravelNotes", 229, 0);
        var body = new StringContent(JsonSerializer.Serialize(user), Encoding.UTF8, "application/json");

        GivenAuthToken("token");

        // Act
        var response = await WhenPost(url, body);

        // Assert
        ShouldBeSuccessHasNoData(response);
    }
    
    [Test]
    public async Task {MethodName}_TravelPlan()
    {
        // Arrange
        var url = $"url";
        var user = Given{MethodName}Param("TravelPlan", 229, 1);
        var body = new StringContent(JsonSerializer.Serialize(user), Encoding.UTF8, "application/json");

        GivenAuthToken("token");
        
        // Act
        var response = await WhenPost(url, body);

        // Assert
        ShouldBeSuccessHasNoData(response);
    }

    private async Task<JsonResultBase?> WhenPost(string url, StringContent body)
    {
        var response = await _client.PostAsync(url, body);
        var content = await response.Content.ReadAsStringAsync();
        //Console.Out.WriteLine(content);
        JsonResultBase? responseBase = JsonConvert.DeserializeObject<JsonResultBase>(content);
        return responseBase;
    }

    private void GivenAuthToken(string token)
    {
        _client.DefaultRequestHeaders.Authorization =
            new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token);
    }

    private static RequestClass Given{MethodName}Param(string type, int? id, int? day)
    {
        var data = new RequestClass();
        data.Type = type;
        data.Param = new Param() { Id= id, Day = day };
        data.ContentFileInfo = new List<string>();
        return data;
    }
}

Last updated