ฉันจะตรวจสอบได้อย่างไรว่าเมธอดถูกเรียกด้วย Moq เพียงครั้งเดียว Verify()
เทียบกับVerifable()
สิ่งที่เป็นจริงที่ทำให้เกิดความสับสน
ฉันจะตรวจสอบได้อย่างไรว่าเมธอดถูกเรียกด้วย Moq เพียงครั้งเดียว Verify()
เทียบกับVerifable()
สิ่งที่เป็นจริงที่ทำให้เกิดความสับสน
คำตอบ:
คุณสามารถใช้Times.Once()
หรือTimes.Exactly(1)
:
mockContext.Verify(x => x.SaveChanges(), Times.Once());
mockContext.Verify(x => x.SaveChanges(), Times.Exactly(1));
วิธีการในคลาสTimes มีดังนี้
AtLeast
- ระบุว่าควรเรียกใช้เมธอดที่เยาะเย้ยเป็นครั้งเท่าต่ำสุดAtLeastOnce
- ระบุว่าควรเรียกใช้เมธอดจำลองหนึ่งครั้งเป็นอย่างต่ำAtMost
- ระบุว่าควรเรียกใช้เมธอดที่เยาะเย้ยครั้งเป็นเวลาสูงสุดAtMostOnce
- ระบุว่าควรเรียกใช้เมธอดจำลองหนึ่งครั้งเป็นค่าสูงสุดBetween
- ระบุว่าควรเรียกใช้เมธอดจำลองระหว่างจากถึงบางครั้งExactly
- ระบุว่าควรเรียกใช้เมธอดที่เยาะเย้ยครั้งละเท่า ๆ กันNever
- ระบุว่าไม่ควรเรียกใช้วิธีการจำลองOnce
- ระบุว่าควรเรียกวิธีการจำลองแบบครั้งเดียวเพียงจำไว้ว่าพวกเขาเป็นวิธีการเรียก; ฉันสะดุดขึ้นเรื่อย ๆ คิดว่ามันเป็นคุณสมบัติและลืมวงเล็บ
var mockContext = new Mock<IContext>()
จะตั้งขึ้น
AtLeast
, AtMost
, Between
หรือExactly
อาจจะมองว่าเป็นสถานที่ให้บริการ ฉันหมายความว่าพวกเขา obv ต้องการพารามิเตอร์เพื่อทำอะไรบางอย่าง
ลองนึกภาพว่าเรากำลังสร้างเครื่องคิดเลขด้วยวิธีหนึ่งในการบวกจำนวนเต็ม 2 ตัว ลองจินตนาการต่อไปว่าความต้องการคือเมื่อเรียกวิธีการเพิ่มจะเรียกวิธีการพิมพ์ครั้งเดียว นี่คือวิธีที่เราจะทดสอบสิ่งนี้:
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);
}
}
และนี่คือการทดสอบจริงพร้อมความคิดเห็นภายในรหัสเพื่อชี้แจงเพิ่มเติม:
[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);
}
}
หมายเหตุ : โดยค่าเริ่มต้น Moq จะทำลายคุณสมบัติและวิธีการทั้งหมดทันทีที่คุณสร้างวัตถุจำลอง แม้ว่าจะไม่มีการโทรSetup
แต่ Moq ก็มีวิธีการต่างๆIPrinter
เพื่อให้คุณสามารถโทรVerify
ได้ อย่างไรก็ตามตามแนวปฏิบัติที่ดีฉันตั้งค่าไว้เสมอเนื่องจากเราอาจจำเป็นต้องบังคับใช้พารามิเตอร์กับวิธีการเพื่อให้เป็นไปตามความคาดหวังบางอย่างหรือค่าตอบแทนจากวิธีการเพื่อให้เป็นไปตามความคาดหวังบางอย่างหรือตามจำนวนครั้งที่มีการเรียกใช้
Verify
ไปTimes.Once
โดยที่ไม่เคยโทรSetup
เลย แน่นอนฉันคาดหวังว่าVerify
จะระเบิดในกรณีนั้น แต่ก็ไม่เป็นเช่นนั้น
Mock
วัตถุ แม้ว่าจะไม่มีการโทรSetup
แต่ Moq ก็มีวิธีการต่างๆIPrinter
เพื่อให้คุณสามารถโทรVerify
ได้ อย่างไรก็ตามตามแนวปฏิบัติที่ดีฉันมักจะตั้งค่าเนื่องจากเราอาจต้องบังคับใช้พารามิเตอร์กับวิธีการหรือค่าที่ส่งคืนจากวิธีการ
Times.Exactly(1)
และมันก็ไม่ล้มเหลวเมื่อเมธอดถูกเรียกสองครั้ง หลังจากเพิ่มSetup
สำหรับวิธีที่เป็นปัญหาเท่านั้นที่ล้มเหลวอย่างถูกต้อง
ตัวควบคุมการทดสอบอาจเป็น:
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);
}
และเมื่อเมธอด DeleteCars เรียกด้วย ID ที่ถูกต้องเราสามารถตรวจสอบได้ว่า Service remove method เรียกว่าหนึ่งครั้งโดยการทดสอบนี้:
[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));
}