How do I verify a method was called exactly once with Moq? The Verify()开发者_如何学Go
vs. Verifable()
thing is really confusing.
You can use Times.Once()
, or Times.Exactly(1)
:
mockContext.Verify(x => x.SaveChanges(), Times.Once());
mockContext.Verify(x => x.SaveChanges(), Times.Exactly(1));
Here are the methods on the Times class:
AtLeast
- Specifies that a mocked method should be invoked times times as minimum.AtLeastOnce
- Specifies that a mocked method should be invoked one time as minimum.AtMost
- Specifies that a mocked method should be invoked times time as maximum.AtMostOnce
- Specifies that a mocked method should be invoked one time as maximum.Between
- Specifies that a mocked method should be invoked between from and to times.Exactly
- Specifies that a mocked method should be invoked exactly times times.Never
- Specifies that a mocked method should not be invoked.Once
- Specifies that a mocked method should be invoked exactly one time.
Just remember that they are method calls; I kept getting tripped up, thinking they were properties and forgetting the parentheses.
Imagine we are building a calculator with one method for adding 2 integers. Let's further imagine the requirement is that when the add method is called, it calls the print method once. Here is how we would test this:
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);
}
}
And here is the actual test with comments within the code for further clarification:
[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);
}
}
Note: By default Moq will stub all the properties and methods as soon as you create a Mock object. So even without calling Setup
, Moq has already stubbed the methods for IPrinter
so you can just call Verify
. However, as a good practice, I always set it up because we may need to enforce the parameters to the method to meet certain expectations, or the return value from the method to meet certain expectations or the number of times it has been called.
Test controller may be :
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);
}
And When DeleteCars method called with valid id, then we can verify that, Service remove method called exactly once by this 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));
}
精彩评论