.NET 幫助

Moq C#(開發人員如何使用)

Kannaopat Udonpant
坎納帕特·烏頓潘
2023年10月29日
分享:

在軟體開發的世界中,測試是一個不可或缺的過程。 它確保您的程式碼按預期運行,並在錯誤到達生產環境之前幫助捕捉它們。 測試的一個重要方面是模擬,對於 C# 測試而言,MOQ 是開發人員工具庫中的一個強大工具。 它支持 lambda 表達式。 MOQ,意指“.NET的模擬物件框架”,簡化了為單元測試創建模擬物件的過程。 在本文中,我們將深入探討 C# 中的 MOQ。

什麼是 MOQ?

MOQ - .NET 模擬框架 是一個用於 .NET 應用程式的模擬框架,允許開發人員快速有效地創建模擬對象。 模擬物件模擬您應用程式中實際物件的行為,使您更容易隔離和測試程式碼的特定部分。 MOQ 簡化了創建和使用這些模擬對象的過程。

MOQ 的主要功能

  • 流暢介面: MOQ 提供了一個流暢且富有表達力的 API,用於設置預期和驗證。 這使您的測試程式碼更具可讀性且更易於理解。
  • 強型別: MOQ 利用 C# 語言功能在定義模擬和期望時提供強型別和 IntelliSense 支援。 這降低了測試中運行時錯誤的機率。
  • 鬆散模擬: MOQ 支援嚴格和鬆散模擬兩者。 寬鬆的模擬允許您創建對任何方法調用都有回應的模擬對象,而嚴格模擬則強制只有預期的方法被調用。
  • 可验证行为: MOQ 允許您驗證在模擬物件上是否以期望的參數和正確的順序呼叫了特定的方法。
  • 回調與返回: 您可以定義回調以執行自定義程式碼,當模擬的方法被調用時,並為模擬的方法指定返回值。

入門 MOQ

在本教程中,我們將探討如何使用MOQ,一個流行的C#模擬框架,來促進單元測試。 我們將透過一個範例來演示如何使用 MOQ 模擬依賴關係,創建並測試一個簡單的 ATM 交易場景。

建立一個新的 C

按照以下步驟創建新項目

  1. 打開Visual Studio,選擇 "檔案" > "新增" > "專案..."

  2. 選擇專案範本,配置設定,然後點選「建立」。

    Moq C#(它如何為開發人員工作)圖 1 - 在 Visual Studio 2022 中創建一個新的控制台應用程式

    假設您正在為自動櫃員機開發軟體,並且需要測試驗證和取款功能。 ATM 依賴兩個介面:IHostBankIHSMModule。 我們想要測試ATMCashWithdrawal類別,這個類別代表ATM的現金提取功能。

    創建兩個介面,IHostBankIHSMModule,它們代表ATM系統的依賴項。 定義相關方法,例如authenticateAmountvalidatePIN

// IHostBank.cs
public interface IHostBank
{
    bool AuthenticateAmount(string accountNumber, int amount);
}

// IHSMModule.cs
public interface IHSMModule
{
    bool ValidatePIN(string cardNumber, int pin);
}
// IHostBank.cs
public interface IHostBank
{
    bool AuthenticateAmount(string accountNumber, int amount);
}

// IHSMModule.cs
public interface IHSMModule
{
    bool ValidatePIN(string cardNumber, int pin);
}
' IHostBank.cs
Public Interface IHostBank
	Function AuthenticateAmount(ByVal accountNumber As String, ByVal amount As Integer) As Boolean
End Interface

' IHSMModule.cs
Public Interface IHSMModule
	Function ValidatePIN(ByVal cardNumber As String, ByVal pin As Integer) As Boolean
End Interface
$vbLabelText   $csharpLabel

建立ATMCashWithdrawal類別,該類別使用上述的相依性來執行ATM操作。 在這堂課中,您將實現一個類似於WithdrawAmount的方法。

// ATMCashWithdrawal.cs
public class ATMCashWithdrawal
{
    private readonly IHSMModule hsmModule;
    private readonly IHostBank hostBank;

    public ATMCashWithdrawal(IHSMModule hsmModule, IHostBank hostBank)
    {
        this.hsmModule = hsmModule;
        this.hostBank = hostBank;
    }
// non static method
    public bool WithdrawAmount(string cardNumber, int pin, int amount)
    {
        if (!hsmModule.ValidatePIN(cardNumber, pin))
        {
            return false;
        }

        if (!hostBank.AuthenticateAmount(cardNumber, amount))
        {
            return false;
        }

        // Withdraw the specified amount and perform other operations
        return true;
    }
}
// ATMCashWithdrawal.cs
public class ATMCashWithdrawal
{
    private readonly IHSMModule hsmModule;
    private readonly IHostBank hostBank;

    public ATMCashWithdrawal(IHSMModule hsmModule, IHostBank hostBank)
    {
        this.hsmModule = hsmModule;
        this.hostBank = hostBank;
    }
// non static method
    public bool WithdrawAmount(string cardNumber, int pin, int amount)
    {
        if (!hsmModule.ValidatePIN(cardNumber, pin))
        {
            return false;
        }

        if (!hostBank.AuthenticateAmount(cardNumber, amount))
        {
            return false;
        }

        // Withdraw the specified amount and perform other operations
        return true;
    }
}
' ATMCashWithdrawal.cs
Public Class ATMCashWithdrawal
	Private ReadOnly hsmModule As IHSMModule
	Private ReadOnly hostBank As IHostBank

	Public Sub New(ByVal hsmModule As IHSMModule, ByVal hostBank As IHostBank)
		Me.hsmModule = hsmModule
		Me.hostBank = hostBank
	End Sub
' non static method
	Public Function WithdrawAmount(ByVal cardNumber As String, ByVal pin As Integer, ByVal amount As Integer) As Boolean
		If Not hsmModule.ValidatePIN(cardNumber, pin) Then
			Return False
		End If

		If Not hostBank.AuthenticateAmount(cardNumber, amount) Then
			Return False
		End If

		' Withdraw the specified amount and perform other operations
		Return True
	End Function
End Class
$vbLabelText   $csharpLabel

建立單元測試專案

現在,讓我們使用 MOQ 模擬相依性,為ATMCashWithdrawal類別創建單元測試。

在您的解決方案中創建一個新的單元測試專案,並將其命名為ATMSystem.Tests

要將 NUnit 測試專案添加到您的 Visual Studio 解決方案中,請按照以下步驟操作:

  1. 在方案上右鍵點擊:在方案總管(通常在右側),右鍵點擊方案名稱。

  2. 新增 > 新專案:從內容選單中選擇「新增」,然後選擇「新專案...」

  3. 建立新專案:在 "新增專案" 對話框中,您可以搜尋 "NUnit" 來找到可用的 NUnit 範本。 選擇如下所示的 NUnit 測試專案。

    Moq C#(對開發人員的運作原理)圖 2 - 在您的解決方案中新增一個新的 NUnit 測試專案。

  4. 配置專案:根據需要配置專案設定,包括專案名稱和位置。

  5. 點擊確定:點擊“創建”或“確定”按鈕將 NUnit 測試項目添加到您的解決方案。

    現在,你在解決方案中有一個單獨的 NUnit 測試專案,可以在其中編寫和管理單元測試。 您還可以添加引用至您想要測試的專案,並在此專案中開始撰寫您的 NUnit 測試案例。

    若要開始在測試專案中使用 MOQ,您需要將 MOQ NuGet 套件新增至您的解決方案。 您可以使用 Visual Studio 中的 NuGet 套件管理器完成此操作,或在套件管理器控制台中運行以下命令:

Install-package moq

此命令將安裝該套件,並將所有必需的依賴項添加到項目中。

使用 NUnit 和 MOQ 撰寫單元測試,以模擬 ATMCashWithdrawal 類別的依賴項 (IHostBankIHSMModule)。

using Moq;
using MOQTestProject;

namespace UnitTest
{
    public class Tests
    {
        ATMCashWithdrawal atmCash;
        [SetUp]
        public void Setup()
        {
            // Arrange
            var hsmModuleMock = new Mock<IHSMModule>();
            hsmModuleMock.Setup(h => h.ValidatePIN("123456781234", 1234)).Returns(true);

            var hostBankMock = new Mock<IHostBank>();
            hostBankMock.Setup(h => h.AuthenticateAmount("123456781234", 500)).Returns(true);
            var atmCash = new ATMCashWithdrawal(hsmModuleMock.Object, hostBankMock.Object); // Object property
        }

        [Test]
        public void WithdrawAmount_ValidTransaction_ReturnsTrue()
        {
            // Act
            bool result = atmCash.WithdrawAmount("123456781234", 1234, 500);

            // Assert
            Assert.IsTrue(result); // Verify method 
        }

        // Add more test cases for different scenarios (e.g., invalid PIN, insufficient funds, etc.)
    }
}
using Moq;
using MOQTestProject;

namespace UnitTest
{
    public class Tests
    {
        ATMCashWithdrawal atmCash;
        [SetUp]
        public void Setup()
        {
            // Arrange
            var hsmModuleMock = new Mock<IHSMModule>();
            hsmModuleMock.Setup(h => h.ValidatePIN("123456781234", 1234)).Returns(true);

            var hostBankMock = new Mock<IHostBank>();
            hostBankMock.Setup(h => h.AuthenticateAmount("123456781234", 500)).Returns(true);
            var atmCash = new ATMCashWithdrawal(hsmModuleMock.Object, hostBankMock.Object); // Object property
        }

        [Test]
        public void WithdrawAmount_ValidTransaction_ReturnsTrue()
        {
            // Act
            bool result = atmCash.WithdrawAmount("123456781234", 1234, 500);

            // Assert
            Assert.IsTrue(result); // Verify method 
        }

        // Add more test cases for different scenarios (e.g., invalid PIN, insufficient funds, etc.)
    }
}
Imports Moq
Imports MOQTestProject

Namespace UnitTest
	Public Class Tests
		Private atmCash As ATMCashWithdrawal
		<SetUp>
		Public Sub Setup()
			' Arrange
			Dim hsmModuleMock = New Mock(Of IHSMModule)()
			hsmModuleMock.Setup(Function(h) h.ValidatePIN("123456781234", 1234)).Returns(True)

			Dim hostBankMock = New Mock(Of IHostBank)()
			hostBankMock.Setup(Function(h) h.AuthenticateAmount("123456781234", 500)).Returns(True)
			Dim atmCash = New ATMCashWithdrawal(hsmModuleMock.Object, hostBankMock.Object) ' Object property
		End Sub

		<Test>
		Public Sub WithdrawAmount_ValidTransaction_ReturnsTrue()
			' Act
			Dim result As Boolean = atmCash.WithdrawAmount("123456781234", 1234, 500)

			' Assert
			Assert.IsTrue(result) ' Verify method
		End Sub

		' Add more test cases for different scenarios (e.g., invalid PIN, insufficient funds, etc.)
	End Class
End Namespace
$vbLabelText   $csharpLabel

在此測試代碼中,我們使用 MOQ 來為 IHSMModuleIHostBank 創建模擬對象,並在測試期間調用時指定它們的行為。

在上述代碼範例中,我們演示了使用MOQ在C#中模擬對象的概念。 我們為 IHSMModuleIHostBank 介面創建模擬對象,模擬它們在單元測試期間的行為。 這使我們能夠通過控制這些模擬對象的響應來隔離並徹底測試ATMCashWithdrawal類別。 通過模擬,我們可以確保我們的程式碼與這些依賴正確互動,使我們的測試更專注、可預測,並有效識別受檢單元程式碼中的問題。 這種做法提高了代碼的整體可靠性、可維護性,使測試代碼更容易。

步驟3 執行測試

  1. 構建您的解決方案以確保所有內容都是最新的。

  2. 在 Visual Studio 中打開測試總管(測試 > 測試總管)。

  3. 在測試總管中點選「全部執行」按鈕來執行您的單元測試。

  4. 檢查測試結果。 您應該看到您撰寫的測試(WithdrawAmount\_ValidTransaction\_ReturnsTrue)通過。

    ![Moq C#(對開發者的運作方式)圖 3 - 要執行測試,首先必須建置解決方案。 成功建置後,請在 Visual Studio 中開啟 "Test Explorer",然後點擊 "Run All" 按鈕來開始您的單元測試執行。

    這樣,我們可以隔離我們想要測試的代碼,並通過有效地模擬依賴項來確保它在各種場景下按預期行為。 這種做法提高了軟體的可靠性和可維護性,使得在開發過程的早期更容易識別和修復問題。

介紹 IronPDF

IronPDF 文檔及功能概覽 是一個強大的 C# 函式庫,讓開發人員能夠在他們的應用程式中處理 PDF 文件。 它提供了廣泛的功能,包括從各種來源(如 HTML、圖像和現有的 PDF)創建、修改和轉換 PDF 文件。 當結合使用前一教程中討論的模擬對象概念時,IronPDF 可成為在單元測試中生成和操作 PDF 文件的有價值工具。

IronPDF 的主要功能是其 HTML 到 PDF 轉換 功能,確保版面和樣式完好無損。 它將網頁內容轉換為PDF,非常適合報告、發票和文件。 此功能支持將 HTML 檔案、URL 和 HTML 字串轉換為 PDF。

using IronPdf;

class Program
{
    static void Main(string[] args)
    {
        var renderer = new ChromePdfRenderer();

        // 1. Convert HTML String to PDF
        var htmlContent = "<h1>Hello, IronPDF!</h1><p>This is a PDF from an HTML string.</p>";
        var pdfFromHtmlString = renderer.RenderHtmlAsPdf(htmlContent);
        pdfFromHtmlString.SaveAs("HTMLStringToPDF.pdf");

        // 2. Convert HTML File to PDF
        var htmlFilePath = "path_to_your_html_file.html"; // Specify the path to your HTML file
        var pdfFromHtmlFile = renderer.RenderHtmlFileAsPdf(htmlFilePath);
        pdfFromHtmlFile.SaveAs("HTMLFileToPDF.pdf");

        // 3. Convert URL to PDF
        var url = "http://ironpdf.com"; // Specify the URL
        var pdfFromUrl = renderer.RenderUrlAsPdf(url);
        pdfFromUrl.SaveAs("URLToPDF.pdf");
    }
}
using IronPdf;

class Program
{
    static void Main(string[] args)
    {
        var renderer = new ChromePdfRenderer();

        // 1. Convert HTML String to PDF
        var htmlContent = "<h1>Hello, IronPDF!</h1><p>This is a PDF from an HTML string.</p>";
        var pdfFromHtmlString = renderer.RenderHtmlAsPdf(htmlContent);
        pdfFromHtmlString.SaveAs("HTMLStringToPDF.pdf");

        // 2. Convert HTML File to PDF
        var htmlFilePath = "path_to_your_html_file.html"; // Specify the path to your HTML file
        var pdfFromHtmlFile = renderer.RenderHtmlFileAsPdf(htmlFilePath);
        pdfFromHtmlFile.SaveAs("HTMLFileToPDF.pdf");

        // 3. Convert URL to PDF
        var url = "http://ironpdf.com"; // Specify the URL
        var pdfFromUrl = renderer.RenderUrlAsPdf(url);
        pdfFromUrl.SaveAs("URLToPDF.pdf");
    }
}
Imports IronPdf

Friend Class Program
	Shared Sub Main(ByVal args() As String)
		Dim renderer = New ChromePdfRenderer()

		' 1. Convert HTML String to PDF
		Dim htmlContent = "<h1>Hello, IronPDF!</h1><p>This is a PDF from an HTML string.</p>"
		Dim pdfFromHtmlString = renderer.RenderHtmlAsPdf(htmlContent)
		pdfFromHtmlString.SaveAs("HTMLStringToPDF.pdf")

		' 2. Convert HTML File to PDF
		Dim htmlFilePath = "path_to_your_html_file.html" ' Specify the path to your HTML file
		Dim pdfFromHtmlFile = renderer.RenderHtmlFileAsPdf(htmlFilePath)
		pdfFromHtmlFile.SaveAs("HTMLFileToPDF.pdf")

		' 3. Convert URL to PDF
		Dim url = "http://ironpdf.com" ' Specify the URL
		Dim pdfFromUrl = renderer.RenderUrlAsPdf(url)
		pdfFromUrl.SaveAs("URLToPDF.pdf")
	End Sub
End Class
$vbLabelText   $csharpLabel

例如,如果您有一個涉及 PDF 生成或處理的專案,可以使用 IronPDF 創建模擬真實場景的 PDF 文件。 這對於測試和驗證您的程式碼如何與 PDF 檔案互動特別有用。 您可以生成具有特定內容、佈局和屬性的模擬 PDF,然後將它們用作測試固定裝置,以確保您的代碼產生所需的 PDF 輸出或正確處理與 PDF 相關的操作。

創建用於生成 PDF 的模擬物件

假設您正在開發一個生成財務報告的應用程式,並且這些報告需要以 PDF 文件的形式保存和分發。 在這種情況下,您可能需要測試 PDF 生成並確保內容和格式正確。

首先,我們需要將 IronPDF 添加到我們的專案中。 在 NuGet 套件管理器控制台中輸入以下命令來安裝 IronPDF。

Install-Package IronPdf

此命令將安裝並添加必要的相依項目到我們的專案中。

以下是如何將IronPDF整合到單元測試過程中的方法:

生成模擬PDF

您可以使用 IronPDF 建立帶有特定內容和樣式的模擬 PDF 文件,以模仿真實的財務報告。 以下範例代碼說明了這些模擬 PDF 如何用作單元測試的測試夾具:

public class PDFGenerator
{
    public void GenerateFinancialReport(string reportData)
    {
        var renderer = new ChromePdfRenderer();
        // Generate the report HTML
        string reportHtml = GenerateReportHtml(reportData);
        PdfDocument pdfDocument = renderer.RenderHtmlAsPdf(reportHtml);
        // Save the PDF to a file or memory stream
        pdfDocument.SaveAsPdfA("FinancialReport.pdf");
    }

    private string GenerateReportHtml(string reportData)
    {
        // Generate the report HTML based on the provided data
        // (e.g., using Razor views or any HTML templating mechanism)
        // Return the HTML as a string

        return "<h1>my Report</h1>";
    }
}
public class PDFGenerator
{
    public void GenerateFinancialReport(string reportData)
    {
        var renderer = new ChromePdfRenderer();
        // Generate the report HTML
        string reportHtml = GenerateReportHtml(reportData);
        PdfDocument pdfDocument = renderer.RenderHtmlAsPdf(reportHtml);
        // Save the PDF to a file or memory stream
        pdfDocument.SaveAsPdfA("FinancialReport.pdf");
    }

    private string GenerateReportHtml(string reportData)
    {
        // Generate the report HTML based on the provided data
        // (e.g., using Razor views or any HTML templating mechanism)
        // Return the HTML as a string

        return "<h1>my Report</h1>";
    }
}
Public Class PDFGenerator
	Public Sub GenerateFinancialReport(ByVal reportData As String)
		Dim renderer = New ChromePdfRenderer()
		' Generate the report HTML
		Dim reportHtml As String = GenerateReportHtml(reportData)
		Dim pdfDocument As PdfDocument = renderer.RenderHtmlAsPdf(reportHtml)
		' Save the PDF to a file or memory stream
		pdfDocument.SaveAsPdfA("FinancialReport.pdf")
	End Sub

	Private Function GenerateReportHtml(ByVal reportData As String) As String
		' Generate the report HTML based on the provided data
		' (e.g., using Razor views or any HTML templating mechanism)
		' Return the HTML as a string

		Return "<h1>my Report</h1>"
	End Function
End Class
$vbLabelText   $csharpLabel

使用模擬 PDF 進行單元測試

我們將編寫測試,使用 IronPDF 生成代表各種報告場景的模擬 PDF。 然後,我們將把我們代碼生成的實際PDF與這些模擬PDF進行比較,以確保內容、格式和結構如預期一致。

internal class PDFGeneratorTests
{
    [Test]
    public void GenerateFinancialReport_CreatesCorrectPDF()
    {
        // Arrange
        var mock = new PDFGenerator();
        var expectedPdf = PdfDocument.FromFile("ExpectedFinancialReport.pdf"); // Load a mock PDF

        // Act
        mock.GenerateFinancialReport("Sample report data");
        var actualPdf = PdfDocument.FromFile("FinancialReport.pdf");

        // Assert
        Assert.AreEqual(actualPdf.ExtractAllText() , expectedPdf.ExtractAllText());
    }

}
internal class PDFGeneratorTests
{
    [Test]
    public void GenerateFinancialReport_CreatesCorrectPDF()
    {
        // Arrange
        var mock = new PDFGenerator();
        var expectedPdf = PdfDocument.FromFile("ExpectedFinancialReport.pdf"); // Load a mock PDF

        // Act
        mock.GenerateFinancialReport("Sample report data");
        var actualPdf = PdfDocument.FromFile("FinancialReport.pdf");

        // Assert
        Assert.AreEqual(actualPdf.ExtractAllText() , expectedPdf.ExtractAllText());
    }

}
Friend Class PDFGeneratorTests
	<Test>
	Public Sub GenerateFinancialReport_CreatesCorrectPDF()
		' Arrange
		Dim mock = New PDFGenerator()
		Dim expectedPdf = PdfDocument.FromFile("ExpectedFinancialReport.pdf") ' Load a mock PDF

		' Act
		mock.GenerateFinancialReport("Sample report data")
		Dim actualPdf = PdfDocument.FromFile("FinancialReport.pdf")

		' Assert
		Assert.AreEqual(actualPdf.ExtractAllText(), expectedPdf.ExtractAllText())
	End Sub

End Class
$vbLabelText   $csharpLabel

在此測試代碼中,我們生成一個模擬 PDF(expectedPdf)來表示預期輸出,並將其與由 PDFGenerator 生成的 PDF(actualPDF)進行比較。 我們已經提取了兩個 PDF 的內容來確認它們是否有相同的內容。

結論

總結來說,在單元測試過程中結合使用MOQ與IronPDF,使我們能夠全面驗證軟體應用的行為。 MOQ 讓我們能夠隔離特定的代碼元件、控制依賴性,並模擬複雜的情境,使我們能夠撰寫專注且可靠的測試。

同時,IronPDF 透過促進 PDF 文件生成和操作,增強了我們的測試能力,確保我們的 PDF 相關功能得到徹底檢查。 通過將這些工具整合到我們的測試工具包中,我們可以自信地開發出既滿足功能又滿足性能要求的穩健和高品質軟體。 這種使用MOQ進行穩健的單元測試與使用IronPDF進行PDF驗證的結合,大大提高了我們應用程序的整體質量和可靠性。

值得注意的是,IronPDF 提供免費試用以測試其功能。 如果您認為它適合您的需求,您可以選擇購買商業授權,這可以讓您在專案中繼續使用IronPDF的功能,並享有隨附授權版本的全部優勢和支持,確保PDF相關功能順利整合到您的應用程式中。

Kannaopat Udonpant
坎納帕特·烏頓潘
軟體工程師
在成為軟體工程師之前,Kannapat 在日本北海道大學完成了環境資源博士學位。在攻讀學位期間,Kannapat 也成為了車輛機器人實驗室的成員,該實驗室隸屬於生物生產工程學系。2022 年,他利用自己的 C# 技能,加入了 Iron Software 的工程團隊,專注於 IronPDF 的開發。Kannapat 珍視這份工作,因為他可以直接向負責撰寫大部分 IronPDF 程式碼的開發人員學習。除了同儕學習外,Kannapat 還享受在 Iron Software 工作的社交方面。當他不在撰寫程式碼或文件時,Kannapat 通常會在 PS5 上玩遊戲或重看《最後生還者》。
< 上一頁
Entity Framework C#(開發人員的工作原理)
下一個 >
C# 網頁框架(開發人員如何使用)