【C#】【xUnit】【Moq】.NET单元测试Mock框架Moq初探!
在TDD开发模型中,经常是在编码的同时进行单元测试的编写,由于现代软件开发不可能是一个人完成的工作,所以在定义好接口的时候我们就可以进行自己功能的开发(接口不能经常变更),而我们调用他人的功能时只需要使用接口即可。
但我们在编写自己的单元测试并进行功能验证的时候,如果接口的实现人还没有完成代码怎么办呢?一般我们可能会自己写一个模拟实现来进行单元测试,这就是我们经常所说的单元测试中的Stub和Mock(关于单元测试的Stub和Mock,可以自己度娘一下,也可以参考https://www.cnblogs.com/TankXiao/archive/2012/03/06/2366073.html, 本文的部分代码来自于这篇博客)。在.net环境中可以使用的Mock框架是Moq,目前版本4.10。
我们使用NuGet安装依赖的库xUnit,Moq等。
我们定义两个接口:
public interface IWebService { void LogError(string msg); } public interface IEmailService { void SendEmail(string a, string b, string c, string d); }
一个类:
public class LogAnalyzer { private IWebService service; private IEmailService email; public IWebService Service { get { return service; } set { service = value; } } public IEmailService Email { get { return email; } set { email = value; } } public void Analyze(string fileName) { if (fileName.Length < 8) { try { service.LogError("the file name is to short" + fileName); } catch (Exception e) { email.SendEmail("From@test.com", "To@test.com", "IWebServiceFailed", e.Message); } } } }
我们要进行这个类的测试,其中两个接口的实现是别人来做。我在自己的单元测试中不想去引用他人的实现,也不想自己写Mock,所以使用框架Moq来创建我想要的对象。
public class LogAnalyzerTest { [Fact(DisplayName = "使用MOQ框架")] public void AnalyzeTest() { var mockWebService = new Mock<IWebService>(); mockWebService.Setup(p => p.LogError(It.Is<string>(str => str.Length > 8))).Throws(new Exception()); var mockEmailService = new Mock<IEmailService>(); var a = mockEmailService.Setup(e => e.SendEmail("From@test.com", "To@test.com", "IWebServiceFailed", It.Is<string>(x=>x != null))); LogAnalyzer log = new LogAnalyzer(); log.Service = mockWebService.Object; log.Email = mockEmailService.Object; log.Analyze("xxx"); mockEmailService.Verify(p => p.SendEmail("From@test.com", "To@test.com", "IWebServiceFailed", It.Is<string>(x => x != null))); } }
这样我就完成了我的单元测试,而不用去关心我的依赖的代码的实现。保证我的功能的正确性。
对上面Mock的说明如下:
第一个模拟LogError抛出异常的代码:
mockWebService.Setup(p => p.LogError(It.Is<string>(str => str.Length > 8))).Throws(new Exception());
第一行,当参数类型是string且长度大于8时正常执行,而长度长于等于8时则抛出异常。他的另一种写法是范型:
mockWebService.Setup(p => p.LogError(It.Is<string>(str => str.Length > 8))).Throws<Exception>();
在我调用分析方法Analyze时传入的字符串不长于8个,就会完成异常抛出异常的功能。
第二个是Email接口的Mock对象,创建如下:
var a = mockEmailService.Setup(e => e.SendEmail("From@test.com", "To@test.com", "IWebServiceFailed", It.Is<string>(x=>x != null)));
因为最后一个参数是异常的Message,所以我们需要动态指定。前三个参数和代码中一致。
最后验证SendEmail有没有执行。这行代码不能放在log.Analyze调用之前。因为这个时候方法还没有调用,单元测试不会通过。并且参数保持一致。如果参数不一致(特别是前三个)也会测试失败。这就是Mock的强大之处。
- 点赞
- 收藏
- 关注作者
评论(0)