我有一个公共静态方法,该方法使用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。显然,这不是做到这一点的方法。我一直在搜索示例,但是它们似乎都利用对象上的方法,并使用实例方法而不是静态方法。
在对使用该方法的方法进行单元测试时,如何控制该方法返回的内容?
您可以使用需要从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;
}
考虑重构以允许更易于维护的设计
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 }
以便可以在需要的地方显式注入它,而不能作为紧密耦合的依赖关系静态使用,可以将其视为代码气味。
我的评论示例: