[英]Unit test for void method with Interface as parameter
單元測試的新手,我有下面的示例代碼,我想為此創建一個單元測試,請建議我應該怎么做才能為此創建一個單元測試? 任何鏈接或指針都有助於開始
public class UserNotification : Work
{
public override void Execute(IWorkContext iwc)
{
throw new InvalidWorkException($"some message:{iwc.Name} and :{iwc.Dept}");
}
}
編輯:使用 MSTest 進行單元測試
因為您的標題特別提到您正在嘗試測試具有void
返回類型的方法; 我推斷您已經用實際返回值測試了方法,因此您已經有了一個測試項目並且知道如何在編寫測試后運行測試。 如果不; Mithgroth 寫的答案很好地解釋了如何開始一般測試。
您的測試由您希望測試的行為定義。 您的代碼段沒有任何行為,因此很難給您具體的答案。
我選擇重寫你的例子:
public class UserNotification : Work
{
public override void Execute(IWorkContext iwc)
{
var splines = iwc.GetSplines();
iwc.Reticulate(splines);
}
}
現在我們有一些我們想要測試的行為。 測試目標是回答以下問題:
調用
Execute
時,UserNotification
獲取所需的樣條並將它們網狀化?
進行單元測試時,您想模擬所有其他東西。 在這種情況下, IWorkContext
是一個外部依賴項,因此應該對其進行模擬。 Mocking 工作上下文使我們能夠輕松配置模擬以幫助進行測試。 當我們運行測試時,我們將傳遞一個充當間諜的IWorkContext
object。 本質上,這個模擬的 object 將:
Reticulate
方法的任何調用,並跟蹤傳遞給它的參數。在深入了解如何模擬之前,我們已經可以概述我們的測試將如何進行到 go:
[Test]
public void ReticulatesTheContextSplines()
{
// Arrange
IWorkContext mockedContext = ...; // This comes later
UserNotification userNotification = new UserNotification();
// Act
userNotification.Execute(mockedContext);
// Assert
// Confirm that Reticulate() was called
// Confirm that Reticulate() was given the result from `GetSplines()`
}
這是你的基本單元測試。 剩下的就是創建我們的模擬。
如果你願意,你可以自己寫這個。 只需創建一個實現IWorkContext
的新 class ,並為其提供更多公共屬性/方法以幫助您跟蹤事物。 一個非常簡單的例子是:
public class MockedWorkContext : IWorkContext
{
// Allows the test to set the returned result
public IEnumerable<Spline> Splines { get; set; }
// History of arguments used for calls made to Reticulate.
// Each call will add an entry to the list.
public List<IEnumerable<Spline>> ReticulateArguments { get; private set; } = new List<IEnumerable<Spline>>();
public IEnumerable<Spline> GetSplines()
{
// Returns the preset splines that the test configured
return this.Splines;
}
// Mocked implementation of Reticulate()
public void Reticulate(IEnumerable<Spline> splines)
{
// Does nothing except record what you passed into it
this.ReticulateArguments.Add(splines);
}
}
這是一個非常簡化的實現,但它完成了工作。 測試現在看起來像這樣:
[Test]
public void ReticulatesTheContextSplines()
{
// Arrange
IEnumerable<Spline> splines = new List<Spline>() { new Spline(), new Spline() }; // Just create some items here, it's random test data.
IWorkContext mockedContext = new MockedWorkContext();
mockedContext.Splines = splines;
UserNotification userNotification = new UserNotification();
// Act
userNotification.Execute(mockedContext);
// Assert - Confirm that Reticulate() was called
mockedContext.ReticulateArguments.Should().HaveCount(1);
// Confirm that Reticulate() was given the result from `GetSplines()`
mockedContext.ReticulateArguments[0].Should().BeEquivalentTo(splines);
}
該測試現在可以准確地測試您的方法的行為。 它使用模擬上下文作為間諜來報告您的被測單元(即UserNotification
)對您傳遞給它的上下文所做的事情。
請注意,我在這里使用的是 FluentAssertions,因為我發現它是最易讀的語法。 隨意使用您自己的斷言邏輯。
雖然您可以編寫自己的模擬; 有 mocking 個庫可以幫助減少樣板。 據我所知,Moq 和 NSubstitute 是最受歡迎的兩個。 我個人更喜歡 NSubstitute 的語法; 但兩者都同樣出色地完成了工作。
首先,您需要一個測試項目以及您的常規項目。 你可以從這三個中選擇:
所有這些都應該在VS2022中有一個項目模板。
xUnit 是一個流行的,所以讓我們選擇它。 測試項目的通常命名約定是YourProject.Tests
。 將UnitTest1.cs
class 重命名為UserNotificationTests.cs
。
就這么簡單,您現在可以開始編寫測試了。 在 xUnit 中,具有[Fact]
屬性的方法是測試方法。
using Xunit;
namespace MyProject.Tests
{
public class UserNotificationTests
{
[Fact]
public void Execute_Should_Throw_InvalidWorkException_With_Message()
{
}
}
}
不要把這些方法當成代碼中的方法,命名要接近英文句子,要像正則句一樣能顯露意圖。
單元測試的經典方法分為三個階段:
讓我們從安排開始吧。
UserNotification
以便我們可以調用Execute()
。IWorkContext
object 以便我們可以傳遞它。 為此,我們將使用NSubstitute 庫。// Don't forget to add using NSubstitute
// Arrange
var userNotification = new UserNotification();
var workContext = Substitute.For<IWorkContext>();
workContext.Name = "testName";
workContext.Dept = "testDept";
現在你行動,並調用你的方法:
// Act
Action act = () => userNotification.Execute(workContext);
最后我們斷言。 我強烈推薦用於斷言的FluentAssertations 庫。
// Assert
act.Should().Throw<InvalidWorkException>()
.WithMessage($"some message:{workContext.Name} and :{workContext.Dept}");
導航到 View > Test Explorer 並運行您的測試,您應該會看到類似於此的內容:
恭喜,您編寫了第一個單元測試。 這是測試代碼的最終版本:
using FluentAssertions;
using NSubstitute;
using System;
using Xunit;
namespace MyProject.Tests
{
public class UserNotificationTests
{
[Fact]
public void Execute_Should_Throw_InvalidWorkException_With_Message()
{
// Arrange
var userNotification = new UserNotification();
var workContext = Substitute.For<IWorkContext>();
workContext.Name = "testName";
workContext.Dept = "testDept";
// Act
Action act = () => userNotification.Execute(workContext);
// Assert
act.Should().Throw<InvalidWorkException>()
.WithMessage($"some message:{workContext.Name} and :{workContext.Dept}");
}
}
public class UserNotification : Work
{
public override void Execute(IWorkContext iwc)
{
throw new InvalidWorkException($"some message:{iwc.Name} and :{iwc.Dept}");
}
}
public abstract class Work
{
public virtual void Execute(IWorkContext iwc) { }
}
public interface IWorkContext
{
public string Name { get; set; }
public string Dept { get; set; }
}
public class InvalidWorkException : System.Exception
{
public InvalidWorkException() { }
public InvalidWorkException(string message) : base(message) { }
public InvalidWorkException(string message, System.Exception inner) : base(message, inner) { }
protected InvalidWorkException(
System.Runtime.Serialization.SerializationInfo info,
System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
}
}
編寫測試感覺與編寫常規代碼有很大不同。 但隨着時間的推移,你會掌握它的竅門。 如何模擬、如何行動、如何斷言,這些可能因您測試的內容而異。 要點是隔離你想要單元測試的主要東西,並模擬rest。
祝你好運!
如果你想使用 nunit,帶有示例的文檔很容易理解,鏈接如下。
而且我認為所有其他單元測試框架都與此類似。
[Test]
public void Execute_WhenCalled_ThrowArgumentException()
{
//Initialize an instance of IWorkContext
var iwc = new WorkContext();
//or use a Mock object, later on in assert use
//userNotification.Execute(iwc.Object)
var iwc = new Mock<IWorkContext>();
var userNotification = new UserNotification();
Assert.Throws(typeof(InvalidWorkException), () =>
{
userNotification.Execute(iwc)
});
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.