簡體   English   中英

您可以對沒有返回類型的void方法進行單元測試嗎?

[英]Can you unit test a void method which doesnt have return type?

我有這個無效的方法,我想進行單元測試,但是不幸的是,我對此沒有太多的經驗。

public void findOccuranceMethod()
{
    string str = "The Haunting of Hill House!";
    Console.WriteLine("String: " + str);
    string occurString = "o";
    string replaceString = "MDDS";

    var array = str.Split(new[] { occurString }, StringSplitOptions.None);
    var count = array.Length - 1;
    string result = string.Join(replaceString, array);

    Console.WriteLine("String after replacing a character: " + result);
    Console.WriteLine("Number of replaces made: " + count);
    Console.ReadLine();

}

這是我要測試的TestClass,但運行時它會不斷加載:

[TestMethod()]
public void findOccuranceMethodTest()
{
    // Arrange
    string expected = "The Haunting MDDSf Hill HMDDSuse!";

    // Act
    var result = new findOccurance();

    // Assert
    Assert.AreEqual(expected, result);
}

正確重構代碼以實現可測試性

對這種邏輯進行編碼和單元測試的正確方法是:

public class SomeProductionClass 
{
    public string findOccuranceMethod(string str) 
    {
        string occurString = "o";
        string replaceString = "MDDS";
        var array = str.Split(new[] { occurString }, StringSplitOptions.None);
        string result = string.Join(replaceString, array);

        return result;
    }
}

[TestMethod()]
public void findOccuranceMethodTest()
{
    // Arrange
    string expected = "The Haunting MDDSf Hill HMDDSuse!";
    var productionClass = new SomeProductionClass();

    // Act
    var result = productionClass.findOccuranceMethod("The Haunting of Hill House!");

    // Assert
    Assert.AreEqual(expected, result);
}

如何測試原始代碼

如果由於某種原因您無法控制生產代碼,則有兩個選擇:

  • 用抽象替換Console

  • 將標准輸入/輸出重新分配給自定義流:這是最不推薦使用的選項,因為它會使設置/清除更加復雜,並且可能會干擾測試運行程序。

無論哪種情況,我們都決不能以會為測試/生產引入單獨分支的方式修改被測代碼。 例如,“如果在測試中運行,則執行A,否則執行B” –在這種情況下,我們實際上不是在測試生產代碼,而是測試它的另一個分支。

用抽象替換控制台

在這里,我們介紹IConsole替代System.Console

public interface IConsole
{
    void WriteLine(string s);
    void ReadLine(); // void is enough for this example
}

// for use in production
public class RealConsole : IConsole
{
    public void WriteLine(string s)
    {
        Console.WriteLine(s); 
    }
    public void ReadLine()
    {
        Console.ReadLine();
    }
}

// for use in unit tests
public class TestConsole : IConsole
{
    public void WriteLine(string s)
    {
        Contents.Add(s);
    }
    public void ReadLine()
    {
    }

    public List<string> Contents { get; } = new List<string>();
}

生產代碼將保留原始帖子中的內容,只不過現在它使用_console作為依賴項:

public class SomeProductionClass
{
    private readonly IConsole _console;

    public SomeProductionClass(IConsole console)
    {
        _console = console;
    }

    public void findOccuranceMethod()
    {
        string str = "The Haunting of Hill House!";
        _console.WriteLine("String: " + str);
        string occurString = "o";
        string replaceString = "MDDS";

        var array = str.Split(new[] { occurString }, StringSplitOptions.None);
        var count = array.Length - 1;
        string result = string.Join(replaceString, array);

        _console.WriteLine("String after replacing a character: " + result);
        _console.WriteLine("Number of replaces made: " + count);
        _console.ReadLine();
    }
}

測試代碼為:

[TestMethod()]
public void findOccuranceMethodTest()
{
    // Arrange
    string expectedString = "The Haunting MDDSf Hill HMDDSuse!";
    int expectedCount = 2;
    var console = new TestConsole();
    var productionClass = new SomeProductionClass(console);

    // Act
    productionClass.findOccuranceMethod();

    // Assert
    Assert.AreEqual(3, console.Contents.Count);
    Assert.AreEqual("String: The Haunting of Hill House!", console.Contents[0]);
    Assert.AreEqual(
        $"String after replacing a character: {expectedString}", 
        console.Contents[1]);
    Assert.AreEqual(
        $"Number of replaces made: {expectedCount}", 
        console.Contents[2]);
}

如果需要保留簽名,可以使用可選參數:

    public void optionSameSignature(Action<string> log = null, Func<string> read = null)
    {
        log = log ?? Console.WriteLine;
        read = read ?? Console.ReadLine;

        string str = "The Haunting of Hill House!";
        log("String: " + str);
        string occurString = "o";
        string replaceString = "MDDS";

        var array = str.Split(new[] { occurString }, StringSplitOptions.None);
        var count = array.Length - 1;
        string result = string.Join(replaceString, array);

        log("String after replacing a character: " + result);
        log("Number of replaces made: " + count);
        read();
    }

    public void Test()
    {
        List<string> lines = new List<string>();
        optionSameSignature(lines.Add, () => "");
    }

從更深層次上講,輸入和輸出是技術細節,因此理想情況下,您將嘗試使用沒有技術細節的業務價值代碼。 這樣的事情可能更接近您的實際需求:

    private static void mybusinessfunction(string input, out int count, out string result)
    {
        string occurString = "o";
        string replaceString = "MDDS";

        var array = input.Split(new[] { occurString }, StringSplitOptions.None);
        count = array.Length - 1;
        result = string.Join(replaceString, array);
    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM