我如何在C#单元测试中伪造内置方法的返回?

问题描述 投票:0回答:3

我有一个公共静态方法,该方法使用Random类的对象生成一个整数。然后将该整数作为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);
    }
  }

我想要嘲笑Random.Next每次在此测试中运行时都返回2。显然,这不是做到这一点的方法。我一直在搜索示例,但是它们似乎都利用对象上的方法,并使用实例方法而不是静态方法。

在对使用该方法的方法进行单元测试时,如何控制该方法返回的内容?

c# unit-testing testing tdd moq
3个回答
2
投票

您可以使用需要从Random类重新定义的任何方法在本地创建自己的System.Random类。

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;
}

1
投票

考虑重构以允许更易于维护的设计

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];
    }
}

这允许在使用和测试主题类时具有更大的灵活性

[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
}

以便可以在需要的地方显式注入它,而不能作为紧密耦合的依赖关系静态使用,可以将其视为代码气味。


0
投票

我的评论示例:

© www.soinside.com 2019 - 2024. All rights reserved.