Как мне подделать возврат встроенного метода в модульном тесте C #? - PullRequest
0 голосов
/ 01 октября 2019

У меня есть публичный статический метод, который использует объект класса Random для генерации целого числа. Затем целое число становится индексом списка. Я хочу проверить логику метода, имея контроль над тем, что возвращает метод 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 toвозвращайте 2 каждый раз, когда он запускается в этом тесте. Очевидно, это не способ сделать это. Я искал примеры, но все они, кажется, используют методы объекта и методы экземпляра вместо статических методов.

Как мне контролировать, что возвращает встроенный метод при модульном тестировании метода, которыйиспользует это?

Ответы [ 3 ]

2 голосов
/ 01 октября 2019

Вы можете создать свой собственный класс 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 голос
/ 01 октября 2019

Рассмотрите возможность рефакторинга для обеспечения более удобного проектирования

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 голосов
/ 01 октября 2019

Пример моего предложения в комментарии:

[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));
    }
}
...