繁体   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