Jak sprawdzić, czy metoda została wywołana dokładnie raz za pomocą Moq? Sprawa Verify()
vs. Verifable()
jest naprawdę zagmatwana.
Jak sprawdzić, czy metoda została wywołana dokładnie raz za pomocą Moq? Sprawa Verify()
vs. Verifable()
jest naprawdę zagmatwana.
Odpowiedzi:
Możesz użyć Times.Once()
lub Times.Exactly(1)
:
mockContext.Verify(x => x.SaveChanges(), Times.Once());
mockContext.Verify(x => x.SaveChanges(), Times.Exactly(1));
Oto metody w klasie Times :
AtLeast
- Określa, że symulowana metoda powinna być wywoływana razy minimum.AtLeastOnce
- określa, że symulowana metoda powinna być wywoływana co najmniej jeden raz.AtMost
- Określa, że symulowana metoda powinna być wywoływana w maksymalnych odstępach czasu.AtMostOnce
- określa, że symulowana metoda powinna być wywoływana maksymalnie jeden raz.Between
- określa, że mockowana metoda powinna być wywoływana między czasami.Exactly
- określa, że symulowana metoda powinna być wywoływana dokładnie razy.Never
- określa, że nie należy wywoływać fałszywej metody.Once
- określa, że symulowana metoda powinna być wywoływana dokładnie jeden raz.Pamiętaj tylko, że są to wywołania metod; Ciągle się potykałem, myśląc, że to właściwości i zapominając o nawiasach.
var mockContext = new Mock<IContext>()
ustawienia tego.
AtLeast
, AtMost
, Between
, lub Exactly
mogą być postrzegane jako własność. Chodzi mi o to, że potrzebują parametru, aby coś zrobić.
Wyobraź sobie, że tworzymy kalkulator z jedną metodą dodawania 2 liczb całkowitych. Wyobraźmy sobie dalej, że wymaganie polega na tym, że wywołanie metody add powoduje jednokrotne wywołanie metody print. Oto jak byśmy to przetestowali:
public interface IPrinter
{
void Print(int answer);
}
public class ConsolePrinter : IPrinter
{
public void Print(int answer)
{
Console.WriteLine("The answer is {0}.", answer);
}
}
public class Calculator
{
private IPrinter printer;
public Calculator(IPrinter printer)
{
this.printer = printer;
}
public void Add(int num1, int num2)
{
printer.Print(num1 + num2);
}
}
A oto rzeczywisty test z komentarzami w kodzie w celu dalszego wyjaśnienia:
[TestClass]
public class CalculatorTests
{
[TestMethod]
public void WhenAddIsCalled__ItShouldCallPrint()
{
/* Arrange */
var iPrinterMock = new Mock<IPrinter>();
// Let's mock the method so when it is called, we handle it
iPrinterMock.Setup(x => x.Print(It.IsAny<int>()));
// Create the calculator and pass the mocked printer to it
var calculator = new Calculator(iPrinterMock.Object);
/* Act */
calculator.Add(1, 1);
/* Assert */
// Let's make sure that the calculator's Add method called printer.Print. Here we are making sure it is called once but this is optional
iPrinterMock.Verify(x => x.Print(It.IsAny<int>()), Times.Once);
// Or we can be more specific and ensure that Print was called with the correct parameter.
iPrinterMock.Verify(x => x.Print(3), Times.Once);
}
}
Uwaga : Domyślnie Moq usunie wszystkie właściwości i metody, gdy tylko utworzysz obiekt Mock. Więc nawet bez wywoływania Setup
, Moq już zdeponował metody, IPrinter
więc możesz po prostu wywołać Verify
. Jednak jako dobrą praktykę zawsze ją konfiguruję, ponieważ może być konieczne wymuszenie parametrów metody, aby spełnić określone oczekiwania, lub wartość zwracaną z metody w celu spełnienia określonych oczekiwań lub liczbę jej wywołań.
Verify
, Times.Once
nigdy nie dzwoniłem Setup
. Z pewnością spodziewałbym Verify
się wybuchu w takim przypadku, ale tak się nie stało.
Mock
obiekt. Więc nawet bez wywoływania Setup
, Moq już zdeponował metody, IPrinter
więc możesz po prostu wywołać Verify
. Jednak w ramach dobrej praktyki zawsze ją konfiguruję, ponieważ może być konieczne wymuszenie parametrów metody lub wartości zwracanej z metody.
Times.Exactly(1)
i nie zawiodło się, gdy metoda została faktycznie wywołana dwukrotnie. Dopiero po dodaniu Setup
dla danej metody nie udało się to poprawnie.
Kontrolerem testu może być:
public HttpResponseMessage DeleteCars(HttpRequestMessage request, int id)
{
Car item = _service.Get(id);
if (item == null)
{
return request.CreateResponse(HttpStatusCode.NotFound);
}
_service.Remove(id);
return request.CreateResponse(HttpStatusCode.OK);
}
A gdy metoda DeleteCars zostanie wywołana z poprawnym identyfikatorem, możemy to sprawdzić, metoda Service remove została wywołana dokładnie raz przez ten test:
[TestMethod]
public void Delete_WhenInvokedWithValidId_ShouldBeCalledRevomeOnce()
{
//arange
const int carid = 10;
var car = new Car() { Id = carid, Year = 2001, Model = "TTT", Make = "CAR 1", Price=2000 };
mockCarService.Setup(x => x.Get(It.IsAny<int>())).Returns(car);
var httpRequestMessage = new HttpRequestMessage();
httpRequestMessage.Properties[HttpPropertyKeys.HttpConfigurationKey] = new HttpConfiguration();
//act
var result = carController.DeleteCar(httpRequestMessage, vechileId);
//assert
mockCarService.Verify(x => x.Remove(carid), Times.Exactly(1));
}