Использование moq для проверки вызова функции с параметрами param

У меня есть интерфейс ILogger с параметрами LogTrace (строковое значение, params object []). Теперь я хочу проверить, что LogTrace вызывается, а строка для журнала содержит некоторый идентификатор. Проблема в том, что ее можно назвать по-разному. Например. 1) LogTrace ( "MyString" + id) 2) LogTrace ( "MyString {0}", id) и т.д.

Есть ли хороший способ с Moq проверить все сценарии? Я могу думать только о создании ручной работы, которая будет форматировать строку, которая будет доступна для проверки.

Ответ 1

mock.Verify( m => m.LogTrace( It.IsAny<string>(), It.IsAny<object[]>() ) );

params object[] передается методу как object[], так что вам просто нужно как-то сопоставить массив (как, например, выше, это принимает что-либо).

Если вам нужен больше контроля над списком, используйте It.Is, который позволяет создать собственный предикат:

 mock.Verify( m => m.LogTrace( It.IsAny<string>(),
            It.Is<object[]>(ps =>
                ps != null &&
                ps.Length == 1 &&
                ps[0] is int &&
                (int)ps[0] == 5
            ) ) );

В этом примере показано, как проверить, не является ли список параметров не пустым и содержит 5 как единственный параметр типа int.

Ответ 2

Вы можете попытаться использовать Callback, но он будет достаточно запутанным (не тестированным):

var mock = new Mock<ILogger>();
string trace = null;
mock.Setup(l => l.LogTrace(It.IsAny<string>(), It.IsAny<object[]>()))
        .Callback((s1, par) =>
            {
                trace = string.Format(s1, par);
            });

//rest of the test

Assert.AreEqual(expected, trace);

Если ILogger имеет небольшой интерфейс, вы можете подумать о том, чтобы запустить заглушку вручную (чтобы сохранить все строки, которые он должен регистрировать), и убедитесь, что в конце теста. Если вы регистрируете несколько строк, это будет более читаемой установкой.

Ответ 3

Я не думаю, что есть простой способ сделать то, что вам нужно здесь. Проблема в том, что вам необходимо убедиться, что определенная комбинация значений будет передана вашему методу, что приведет к множеству различных проверяемых сценариев:

  • Строка содержит ID И параметры содержат ID-pass
  • Строка содержит ID И параметры не содержат ID-pass
  • Строка не содержит ID И параметры содержат ID = pass
  • Строка не содержит ID AND параметры не содержат ID-fail

Однако Moq не поддерживает подобные условные выражения между различными аргументами вашего проверяемого метода. Одним из возможных решений является проверка отсутствия id, а не его присутствие в любом аргументе. Попробуйте что-то вроде:

mock.Verify(m => m.LogTrace(
    It.Is<string>(s => !s.Contains(id)), 
    It.Is<object[]>(o => !o.Contains(id))), Times.Never());

То, что мы здесь делаем, - это проверка того, выполняется ли условие сбоя, т.е. ваша строка не содержит идентификатора, а также массив объектов. Мы используем Times.Never(), чтобы убедиться, что эта ситуация никогда не произойдет.

Имейте в виду, однако, что код может быть не очевидным на первый взгляд; убедитесь, что вы правильно объяснили свое намерение, как только вы его напишете.