Moq是我最熟悉的Mocking框架,过去曾经短暂地切换到RhinoMocks。但总归还是回到Moq。倒不是因为觉得Moq能比RhinoMock要好多少,主要还是因为熟悉。用了Moq这么些年头,今天才决定开始写篇文章,讲讲Moq的一些具体使用方法。假设大家都有一定.Net的基础,所以在文章里我就不再多说编程的技巧。Generic programming和Lamda Expression是Moq框架使用最频繁的两个技术,如果哪位朋友想更多地了解这两个技术,MSDN上的文章《Generics (C# programming guide)》和《Lamda Expressions (C# programming guide)》是很好的切入点。一直坚定地认为代码是程序员交流的最好语言,以我个人的经验,要学习使用一个新的框架,与其阅读长篇大论,我更加倾向与在StackOverflow或是CSDN上找一段用例。所以在这篇文章里,我尽量多说代码少说话,希望这篇文章能成为大家收藏夹里的一篇有用的参考。
本文提到的所有的代码都在GitHub里了,有需要的朋友请随意下载
GitHub:https://github.com/gaogang/example.moq.git
[Test] public void RegisterPatient_RegisterAPatient_ShouldCallSavePatientOnRepository() { // Arrange var repository = new Mock<IPatientRepository>(); var expected = "Test"; // 检验: // 1. IPatientRepository的SavePatient函数会被调用 // 2. SavePatient的入参和预期一致 repository.Setup(r => r.SavePatient(It.Is<Patient>(p => p.PatientName == expected))) .Verifiable(); var target = new PatientRegistrationService(repository.Object); // Action target.RegisterPatient(expected); // Assert repository.Verify(); }
<span style="font-weight: normal;">[Test] public void RegisterPatient_RegisterAPatient_ShouldReturnPatientId() { // Arrange var repository = new Mock<IPatientRepository>(); var expected = 111111; repository.Setup(r => r.SavePatient(It.IsAny<Patient>())) // 模拟SavePatient函数的返回值 .Returns(expected); var target = new PatientRegistrationService(repository.Object); // Action var actual = target.RegisterPatient("Anything"); // Assert Assert.AreEqual(expected, actual, "RegisterPatient应该返回IPatientRepository.SavePatient的返回值"); }</span>
[Test] public void RegisterPatients_RegisterMultiplaePatients_ShouldCallSaveOnRepositoryMultipleTimes() { // Arrange var repository = new Mock<IPatientRepository>(); var expected = new Collection<string> { "A", "B", "C", "D" }; var actual = new Collection<string>(); repository.Setup(r => r.SavePatient(It.IsAny<Patient>())) // 在Callback函数里把SavePatient的入参添加到actual列表中 .Callback<Patient>(p => actual.Add(p.PatientName)); var target = new PatientRegistrationService(repository.Object); // Action target.RegisterPatients(expected); // Assert CollectionAssert.AreEqual(expected, actual, "期待集合和实际集合应该完全相符"); }
<pre name="code" class="csharp"> [Test] public void AddPatien_CreateNewPatientRecord_ShouldRegisterThePatientAndUpdatePatientList() { // Arrange var presenterMock = new Mock<PatientListPresenter>(); var patient = "Test Patient"; // CallBase设为真时,Moq允许局部替换 presenterMock.CallBase = true; // 当PatientListPresenter的RegisterPatient函数被调用时,我们要验证AddPatient会: // 1. 调用RegisterPatient函数 // 2. 调用UpdatePatientList函数 presenterMock.Setup(p => p.RegisterPatient(It.Is<string>(n => n == patient))).Verifiable(); presenterMock.Setup(p => p.UpdatePatientList()).Verifiable(); // Action presenterMock.Object.AddPatient(patient); // CallBase设为真时 // 我们可以这样来直接调用AddPatient函数 // Assert presenterMock.Verify(); }
[Test] public void Save_RegisterNewPatient_ShouldGetPatientNameFromView() { // Arrange var registrationService = new Mock<IPatientRegistrationService>(); var view = new Mock<IPatientRegistrationFormView>(); var patientName = "Test Patient"; // 设置PatientName属性的Getter来返回测试用的病人姓名 view.SetupGet(v => v.PatientName).Returns(patientName).Verifiable(); // RegisterPatient用的病人姓名应该与PatientName属性的值一致 registrationService.Setup(r => r.RegisterPatient(It.Is<string>(n => n == patientName))) .Verifiable(); var presenter = new PatientRegistrationFormViewPresenter(view.Object, registrationService.Object); // Action presenter.Save(); // Assert view.Verify(); registrationService.Verify(); }
[Test] public void Load_LoadPatientInfo_ShouldSetPatientNameToView() { // Arrange var registrationService = new Mock<IPatientRegistrationService>(); var view = new Mock<IPatientRegistrationFormView>(); var patientName = "Test Patient"; // 插入GetPatient的返回值 registrationService.Setup(r => r.GetPatient()).Returns(patientName) .Verifiable(); // 设置PatientName属性的Setter设置值应当与GetPatient返回值一致病人姓名 view.SetupSet(v => v.PatientName = It.Is<string>(n => n == patientName)) .Verifiable(); var presenter = new PatientRegistrationFormViewPresenter(view.Object, registrationService.Object); // Action presenter.Load(); // Assert view.Verify(); registrationService.Verify(); }
[Test] public void RegisterPatient_RegisterAPatient_VerifyCallSavePatientOnRepository() { // Arrange var repository = new Mock<IPatientRepository>(); var expected = "Test"; var target = new PatientRegistrationService(repository.Object); // Action target.RegisterPatient(expected); // Assert // 检验: // 1. IPatientRepository的SavePatient函数会被调用 // 2. SavePatient的入参和预期一致 // 3. SavePatient只会被调用一次 repository.Verify(r => r.SavePatient(It.Is<Patient>(p => p.PatientName == expected)), Times.Once); }
[Test] public void Save_RegisterNewPatient_ShouldGetPatientNameFromView() { // Arrange var registrationService = new Mock<IPatientRegistrationService>(); var view = new Mock<IPatientRegistrationFormView>(); var presenter = new PatientRegistrationFormViewPresenter(view.Object, registrationService.Object); // Action presenter.Save(); // Assert view.Verify(v => v.PatientName, Times.Once); }
[Test] public void Load_LoadPatientInfo_ShouldSetPatientNameToView() { // Arrange var registrationService = new Mock<IPatientRegistrationService>(); var view = new Mock<IPatientRegistrationFormView>(); var patientName = "Test Patient"; // 插入GetPatient的返回值 registrationService.Setup(r => r.GetPatient()).Returns(patientName); var presenter = new PatientRegistrationFormViewPresenter(view.Object, registrationService.Object); // Action presenter.Load(); // Assert view.Verify(v => v.PatientName == patientName, Times.Once); }