簡體   English   中英

如何在 C# 單元測試中偽造內置方法的返回?

[英]How do I fake the return of a built-in method in C# unit test?

我有一個公共 static 方法,它使用隨機 class 的 object 來生成 Z157DB7DF53002357551ZD366 然后將 integer 設為 List 的索引。 我想測試方法的邏輯,同時控制隨機對象的 Next 方法返回的內容。

public class RandomGenerator
  {

    // a static method which produces a random selection from a List
    public static string GetRandomListMember(List<string> anyStringList)
    {
      Random rand = new Random();
      int randomInt = rand.Next(anyStringList.Count);
      return anyStringList[randomInt];
    }
  }

從我的 MSTest 文件中:

[TestClass]
  public class RandomGeneratorSpec
  {
    [TestMethod]
    public void GetRandomListMember_ListOfStrings()
    {
      List<string> strings = new List<string> { "red", "yellow", "green" }; 

      Mock<Random> mockRandom = new Mock<Random>();
      mockRandom.Setup(rand => rand.Next(strings.Count)).Returns(() => 2); // 2!

      string result = RandomGenerator.GetRandomListMember(strings);

      Assert.AreEqual("green", result);
    }
  }

我希望 mockRandom.Next 每次在此測試中運行時返回 2 。 顯然,這不是這樣做的方法。 我一直在搜索示例,但它們似乎都使用 object 上的方法和實例方法而不是 static 方法。

在對使用它的方法進行單元測試時,如何控制內置方法返回的內容?

您可以使用從System.Random class 重新定義的任何方法在本地創建自己的Random class。

class Program
{
    static void Main(string[] args)
    {
        var rand = new Random();
        int next = rand.Next(); // uses my local Random class.
    }
}

class Random
{
    public int Next() => 2;
}

考慮重構以實現更可維護的設計

public class RandomGenerator {
    private readonly Random random;

    public RandomGenerator(Random random = default(Random)) {
        this.random = random ?? new Random();
    }

    public string GetRandomListMember(List<string> anyStringList) {
        int randomInt = random.Next(anyStringList.Count);
        return anyStringList[randomInt];
    }
}

這允許更靈活地使用和測試主題 class

[TestClass]
public class RandomGeneratorSpec {
    [TestMethod]
    public void GetRandomListMember_ListOfStrings() {
        //Arrange
        List<string> strings = new List<string> { "red", "yellow", "green" };
        string expected = "green";
        Mock<Random> mockRandom = new Mock<Random>();
        mockRandom.Setup(rand => rand.Next(strings.Count)).Returns(() => 2); // 2!
        var subject = new RandomGenerator(mockRandom.Object);

        //Act
        string actual = subject.GetRandomListMember(strings);

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

理想情況下,如果將此生成器用作服務,它應該有自己的抽象

public interface IRandomGenerator {
    string GetRandomListMember(List<string> anyStringList);
}

public class RandomGenerator : IRandomGenerator {
    //...omitted for brevity
}

這樣它就可以在需要的地方顯式注入,而不是靜態地用作緊密耦合的依賴項,這可以被認為是代碼異味。

我在評論中的建議示例:

[TestClass]
public class RandomGeneratorSpec
{
    [TestMethod]
    public void GetRandomListMember_ListOfStrings()
    {
        List<string> strings = new List<string> { "red", "yellow", "green" }; 

        string result = RandomGenerator.GetRandomListMember(strings);

        Assert.IsTrue(strings.Contains(result));
    }
}

暫無
暫無

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

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