Как проверить другой метод в классе, был вызван с использованием Moq
Это кажется чем-то простым, но я не могу заставить его работать.
У меня есть класс с методом Save, который просто вызывает другой метод ShouldBeCalled(). Я хочу проверить, что если я вызываю Save(), то другой метод ShouldBeCalled() выполняется хотя бы один раз. Я думал, что могу сделать следующее.
public class ClassA
{
public virtual void Save()
{
ShouldBeCalled();
}
public virtual void ShouldBeCalled()
{
//This should get executed
}
}
[TestFixture]
public class ClassA_Test
{
[Test]
public void Save_Should_Call_ShouldBeCalled()
{
var mockClassA = new Mock<ClassA>();
mockClassA.Object.Save();
mockClassA.Verify(x => x.ShouldBeCalled(), Times.AtLeastOnce());
}
}
Но я получаю исключение "Ожидаемое обращение к макету хотя бы один раз, но никогда не выполнялся: x = > x.ShouldBeCalled()"
Это всего лишь предположение, но Мок переопределяет метод Save() с его собственной версией, которая игнорирует все, что у меня есть внутри метода реального объекта Save().
Ответы
Ответ 1
У вас возникла эта проблема, потому что вы издеваетесь над тем, что вы тестируете. Это не имеет смысла.
Вы правы, что Moq заменит реализацию вашего метода своим. Причина в том, что вы должны использовать Moq для издевательства над тем классом, который вы тестируете, а не с классом, который вы тестируете сами.
Этот тест был бы уместным, если бы ваш код был разработан таким образом:
public class ClassA
{
BusinessLogicClass bl;
public ClassA(BusinessLogicClass bl)
{
this.bl = bl;
}
public void Save()
{
bl.ShouldBeCalled();
}
}
public class BusinessLogicClass
{
public virtual void ShouldBeCalled()
{
//This should get executed
}
}
И вот правильный тест этого метода:
[TestFixture]
public class ClassA_Test
{
[Test]
public void Save_ShouldCallShouldBeCalled()
{
//Arrange
var mockBLClass = new Mock<BusinessLogicClass>();
mockBLClass.Setup(x => x.ShouldBeCalled()).Verifyable();
//Act
ClassA classA = new ClassA(mockBLClass.Object);
classA.Save();
//Assert
mockBLClass.VerifyAll();
}
}
Ключевым уроком здесь является то, что вы издеваетесь/заглушаете то, что должен выполнить ваш тест, а не то, что вы сами тестируете.
Надеюсь, это поможет,
Андерсон
Ответ 2
Попробуйте использовать CallBase = true, а затем false. Я запустил ваш код, и он работает.
var mockClassA = new Mock<ClassA>();
mockClassA.CallBase = true;
mockClassA.Object.Save();
mockClassA.CallBase = false;
mockClassA.Verify(x => x.ShouldBeCalled(), Times.AtLeastOnce());
Ответ 3
Да, это можно сделать. Однако вам нужно добавить строку кода для отслеживания Moq, действительно ли был вызван метод ShouldBeCalled.
Будет работать следующее:
var mockClassA = new Mock<ClassA>();
mockClassA.Setup(x => x.ShouldBeCalled()).Verifiable();
mockClassA.Object.Save();
mockClassA.Verify(x => s.ShouldBeCalled(), Times.AtLeastOnce());
Метод установки устанавливает ожидания. Когда вы вызываете Verify, вы просите Moq проверить эти ожидания. Если вы не создаете вызов Setup для создания ожиданий для метода ShouldBeCalled, тогда Moq не считает его отслеживаемым и, следовательно, терпит неудачу, когда вы попытаетесь подтвердить его.
Ответ 4
Вы можете заглушить методы в тестируемой системе, используя CallBase
.
[TestFixture]
public class ClassA_Test
{
[Test]
public void Save_Should_Call_ShouldBeCalled()
{
// Arrange
var mockClassA = new Mock<ClassA>();
mockClassA.CallBase = true; // this will call real methods unless the method is mocked/stubbed.
mockClassA.Setup(a => a.ShouldBeCalled());
// Act
mockClassA.Save();
// Assert
mockClassA.Verify(a => a.ShouldBeCalled(), Times.Once());
}
}