• 单元测试与解耦


    单元测试与解耦

    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.

     
     
  • 相关阅读:
    网络层协议
    交换机原理
    复习
    网络布线和数据转换
    计算机网络参考模型
    Linux常用命令2
    Linux常用命令1
    服务器分类、硬盘分类
    TCP三次握手和断开四次挥手
    向HDFS中指定的文件追加内容,由用户指定内容追加到原有文件的开头或结尾。
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3812771.html
Copyright © 2020-2023  润新知