分享

.NET单元测试的艺术测试代码(下)

 weijianian 2016-08-07


来源:周旭龙 

链接:http://www.cnblogs.com/edisonchou/p/5467573.html


此模式的要点在于:你不是具体地测试一个类,而是测试产品代码中的一个接口或者基类。


当然,在.NET中我们也可以通过泛型来实现此模式,例如下面的代码:


public abstract class GenericParserTestswhere T : IStringParser // 01.定义参数的泛型约束

    {

        protected abstract string GetInputHeaderSingleDigit();

        protected abstract string GetInputHeaderWithMinorVersion();

        protected abstract string GetInputHeaderWithRevision();

 

        // 02.返回泛型变量而非接口

        protected T GetParser(string input)

        {

            // 03.返回泛型

            return (T)Activator.CreateInstance(typeof(T), input);

        }

 

        [Test]

        public void TestGetStringVersionFromHeader_SingleDigit_Found()

        {

            string input = GetInputHeaderSingleDigit();

            T parser = GetParser(input);

 

            bool result = parser.HasCorrectHeader();

            Assert.AreEqual(false, result);

        }

 

        [Test]

        public void TestGetStringVersionFromHeader_WithMinorVersion_Found()

        {

            string input = GetInputHeaderWithMinorVersion();

            T parser = GetParser(input);

 

            bool result = parser.HasCorrectHeader();

            Assert.AreEqual(false, result);

        }

 

        [Test]

        public void TestGetStringVersionFromHeader_WithRevision_Found()

        {

            string input = GetInputHeaderWithRevision();

            T parser = GetParser(input);

 

            bool result = parser.HasCorrectHeader();

            Assert.AreEqual(false, result);

        }

    }

 

    public class DBLogStringParserTests : GenericParserTests

    {

        protected override string GetInputHeaderSingleDigit()

        {

            return 'Header;1';

        }

 

        protected override string GetInputHeaderWithMinorVersion()

        {

            return 'Header;1.1';

        }

 

        protected override string GetInputHeaderWithRevision()

        {

            return 'Header;1.1.1';

        }

    }



二、优秀单元测试的支柱


要编写优秀的单元测试,它们应该同时具有 可靠性可维护性 及 可读性


2.1 编写可靠的测试


一个可靠的测试能让你觉得自己对事态了如指掌,能够从容应对。以下是一些指导原则和技术:


(1)决定何时删除或修改测试


一旦测试写好并通过,通常我们不应该修改或删除这些测试,因为它们是我们得绿色保护网。但是,有时候我们还是需要修改或者删除测试,所以需要理解什么情况下修改或删除测试会带来问题,什么情况下又是合理的。一般来说,如果有产品缺陷、测试缺陷、语义或者API更改或者是由于冲突或无效测试,我们需要修改和删除测试代码。


(2)避免测试中的逻辑


随着测试中逻辑的增多,出现测试缺陷的几率就会呈现指数倍的增长。如果单元测试中包含了下列语句就是包含了不应该有的逻辑:


  • switch、if或else语句;


  • foreach、for或while循环;


这种做法不值得推荐,因为这样的测试可读性较差,也比较脆弱。通常来说,一个单元测试应该是一系列方法的调用和断言,但是不包含控制流程语句,甚至不应该将断言语句放在try-catch中


(3)只测试一个关注点


如果我们的单元测试对多个对象进行了断言,那么这个测试有可能测试了多个关注点。在一个单元测试中验证多个关注点会使得事情变得复杂,却没有什么价值。你应该在分开的、独立的单元测试中验证多余的关注点,这样才能发现真正失败的地方。


(4)把单元测试和集成测试分开


掐面讨论了测试的绿色安全区,我们需要的就是准备一个单独的单元测试项目,项目中仅包含那些在内存中运行,结果稳定,可重复执行的测试。


(5)用代码审查确保代码覆盖率


如果覆盖率低于20%,说明我们缺少很多测试,我们不会知道下一个开发人员将怎么修改我们得代码。如果没有回失败的测试,可能就不会发现这些错误。


2.2 编写可维护性的测试


可维护性是大多数开发者在编写单元测试时面对的核心问题之一。为此我们需要:


(1)只测试公共契约


(2)删除重复测试(去除重复代码)


(3)实施测试隔离


测试隔离的基本概念是:一个测试应该总是在它自己的小世界中运行,与其他类似或不同的工作的测试隔离,甚至不知道其他测试的存在


2.3 编写可读性的测试


不可读的测试几乎没有任何意义,它是我们向项目的下一代开发者讲述的故事,帮助开发者理解一个应用程序的组成及其开端。


(1)单元测试命名


这个前面我们讨论过,应该包括三部分:被测试方法名_测试场景_预期行为,如果开发人员都是用这种规范,其他的开发人员就能很容易进入项目,理解测试。


(2)变量命名


通过合理命名变量,你可以确保阅读测试的人可以尽快地理解你要验证什么(相对于理解产品代码中你想要实现什么)。请看下面的一个例子:


[Test]

    public void BadlyNameTest()

    {

        LogAnalyzer log = new LogAnalyzer();

        int result = log.GetLineCount('abc.txt');

 

        Assert.AreEqual(-100, result);

    }

 

    [Test]

    public void GoodNameTest()

    {

        LogAnalyzer log = new LogAnalyzer();

        int result = log.GetLineCount('abc.txt');

        const int COULD_NOT_READ_FILE = -100;

 

        Assert.AreEqual(-COULD_NOT_READ_FILE, result);

    }


经过改进后,我们会很容易理解这个返回值的意义。


(3)有意义的断言


只有当测试确实需要,并且找不到别的办法使测试更清晰时,你才应该编写定制的断言信息。编写好的断言信息就像编写好的异常信息,一不小心就会犯错,使读者产生误解,浪费他们的时间。


(4)断言和操作分离


为了可读性,请不要把断言和方法调用写在同一行。


// 断言和操作写在了同一行

    Assert.AreEqual(-COULD_NOT_READ_FILE, log.GetLineCount('abc.txt'));


三、小结


这一篇我们学习了:


  • 尽量将测试自动化,尽可能多次地运行测试,尽可能持续地进行产品交付;


  • 把集成测试和单元测试分开,为整个团队构建一个绿色安全区,该区域中所有的测试都必须通过;


  • 按照项目和类型组织测试,把测试分别放在不同的目录、文件夹或者命名空间中;


  • 使用测试类层次,对一个层次中相关的几个类进行同一组测试,或者对共享一个通用接口或者基类的类型进行同一组测试;


  • 优秀单元测试具有三大支柱:可读性、可维护性与可靠性,它们相辅相成。


  • 如果人们能读懂你的测试,就能理解和维护测试,如果测试能够通过,它们也会信任测试。一旦实现这个目标,你就能知道系统是否正常工作,具有了处理变更和在需要时修改代码的能力;


参考资料



(1)Roy Osherove 著,金迎 译,《单元测试的艺术(第2版)》


.NET单元测试系列文章:



    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多