zoukankan      html  css  js  c++  java
  • 单元测试与解耦

    1.标题是什么意思?

    1.1什么是单元测试?

    单元测试,目的是为了保证代码的质量;

    1.2什么是解耦?

    解耦,目的是为了方便单元测试。当然,另一个目的是为了保持程序的扩展性

    思想工具:为了同时达到单元测试与代码解耦(或者称为设计优良的OO代码),那么依赖注入的思想是必不可少的工具。

    • 之所以说是思想,从设计的角度来说,这确实是需要思想上的超越;
    • 之所以说是工具,是因为有许多工具可以实现这一思想,如Ninject,Unity。

    简要如下图所示:

    clip_image002

    2.解除外部依赖及实践

    外部依赖:配置文件、WS、数据库、IO等,可控性较差,是集成测试的接缝点。

    为什么要解除外部依赖,对于一个函数来说,只关注某一功能(即SRP,除非你想把所有的事情在一个方法内做完,但这不是OO,也没有讨论的价值)。

    2.1.耦合的代码

    Eg:调用一个Web服务,最后发送邮件,但如果邮件服务挂了,剩余的逻辑就无法判断了,所以,邮件服务是一个外部依赖,要模拟,或者打桩。

    代码清单1:——常规耦合的代码

    public class WebService
    {
        public void KaoQinSign(string userName,string from, string to)
        {
            //check the argument
            //validate
            // do something logistic
            MailHelper.SendMail("some content", from, to);
        }
    }
     
    public class MailHelper
    {
        public static void SendMail(string content, string from, string to)
        {
            //...
        }
    }

    对于上述的KaoQinSign方法的写法,常见,但不易测试,严格来说,在TDD开发中是不容出现的,根本原因是静态方法的存在,阻止了可测试性,当然,对于Wrapper模式就另当别论

    考虑一个问题:如果KaoQinSign执行到MailHelper.SendMail方法,但运行时邮件系统不知道出了什么问题,异常了。

    我通常的做法:将焦点转移到了MailHelper.SendMail方法,修复之后,然后再回到KaoQinSign进行调试,如果MailHelper.SendMail有问题,继续往前。——随着方法调用的层次越来越深,焦点转移的次数越来越远,Bug率会很高。一般来说,调试的成功率和工作经验成反比。

    2.2 接口注入

    代码清单2:使用接口注入来解耦

    public class WebService
    {
        public void KaoQinSign(IMail mail,string userName, string from, string to)
        {
            //check the argument
            //validate
            // do something logistic
            mail.SendMail("some content", from, to);
        }
    }
     
    public interface IMail
    {
        void SendMail(string content, string from, string to);
    }
     
    public class MailStub : IMail
    {
        public void SendMail(string content, string from, string to) { }
    }

    上述代码,解除了对外部邮件系统的依赖,使KaoQinSign具有可测试性,如果对模拟框架有所了解,那么使用Moq就可以轻松地模拟一个IMail接口,从而使代码开发和测试能够一路向前。

    2.3 模拟与测试

    代码清单3:使用模拟框架进行方法的测试

    [TestFixture]
    public class WebServiceTests
    {
        [Test]
        public void Method1_When_Exception_Will_SendMail()
        {
            WebService ws = new WebService();
            //模拟邮件服务
            Moq.Mock<IMail> mockMail = new Moq.Mock<IMail>();
            //Verifiable表示:将要验证SendMail是否被调用
            mockMail.Setup(zw => zw.SendMail(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>())).Verifiable();
                
            ws.KaoQinSign(mockMail.Object,,"5299530", "One", "Other");
     
            //验证是否被调用
            mockMail.Verify();
        }
    }

    代码清单3,模拟邮件服务意思是这个服务是假的,用来确保这个方法通过的。

    总结

    单元测试的目的——确保(专业点来说称为断言)某一分支能够正确地执行。

    耦合的常见:

    • 静态方法;
    • 一个函数干了几百件事情;
    • 一个函数内容有几百个流程。

    而解除外部依赖是常用的OO编码方法。而上述的静态方法就是典型的,符合二八定律。

    使用

    • SRP确保函数功能的唯一性;
    • 针对接口编程(IOC);
    • 使编码具有可测试性(提取接口以及依赖注入)

    才是TDD的最佳实践,同时,TDD是开发能够有效地横向覆盖(BFS式前进),而不需要使用DFS式地向前开发、调试,从而避免了DFS带来的大脑爆栈。~~~ come from hp.

  • 相关阅读:
    PAT B1045 快速排序 (25 分)
    PAT B1042 字符统计 (20 分)
    PAT B1040 有几个PAT (25 分)
    PAT B1035 插入与归并 (25 分)
    PAT B1034 有理数四则运算 (20 分)
    PAT B1033 旧键盘打字 (20 分)
    HDU 1231 最大连续子序列
    HDU 1166 敌兵布阵
    HDU 1715 大菲波数
    HDU 1016 Prime Ring Problem
  • 原文地址:https://www.cnblogs.com/pengzhen/p/3812654.html
Copyright © 2011-2022 走看看