偏头痛杨的为什么我们要写单元测试用例?

版权声明:偏头痛杨:本文为博主原创文章,未经作者允许不得转载,版权必究! https://blog.csdn.net/piantoutongyang/article/details/80262443
前戏
一些程序员会对单元测试用例存在误区,例如:

~这东西没啥用,就是走个形式主义。

~认为测试用例不应该是开发人员来写,测试不应该是测试工程师的事情吗?
我们是开发程序的,为什么要管测试的事情?抢他们饭碗这样真的好吗?

~每次都要先写完代码,集成测试都通过之后,然后再补单元测试用例,
为的是可笑的leader要求的测试覆盖率达标。

~为什么我还要写一遍单元测试用例?就直接拿UI或集成好的功能手动测试呗。
或者直接手动在main()里写代码进行测试呗,用眼睛看一下对不对不就好了吗?
在main()中测试一下没有问题就OK了,为什么还非得写到junit类中?明明是画蛇添足嘛。

~一个方法只需要一个正确的测试用例即可。。。

~认为我的代码绝对不会有问题,那么简单的功能,肯定不会出错的,我有自信,不用写测试用例。

~写测试用例好麻烦,要准备前置步骤,准备数据,增加工作量,有时还要加班。

我想大多数程序员初期都会有上述的想法,包括当年的我在内。
下面我们就来分析一下,到底什么是单元测试,为什么要写单元测试,单元测试的优劣,走着。


什么是单元测试?
首先,测试的目的是为了保证程序的运行是正确的,是符合业务逻辑的,是可以被正常使用的。
一般情况下,产品经理&需求提供方会召开需求会议,告诉team大家要做什么,目标是什么。
之后,测试team要拿着这些需求去分解成测试用例,而开发team要将需求分解成任务,以便开发。
两者同时进行,在遇到歧义点时,主动发起沟通,确保测试team与开发team不存在太大的歧义点。

再科普一下测试用例,从宏观角度来讲,测试用例分为两大类:
由测试工程师编写的测试用例,由开发工程师编写的测试用例。
测试工程师可能要先编写一些场景或用例用于测试,
写在word文档中或录入到类似于禅道这样的项目管理软件中,并且还要进行测试用例评审,
以保证正确的需求理解以及可以覆盖到相关的业务场景。

此外,测试工程师还需要通过一些测试软件例如jmeter、loadrunner或其他UI自动化工具
来编写能运行的测试用例,来进行自动化测试。
什么?你说你要手动测试?那每次发版&上线前真的是要累趴下一批又一批测试工程师了。
所谓的以手动化为耻,以自动化为荣。

开发工程师这边写的测试用例,也可以称为单元测试用例,引入了今天的主题,
什么是开发工程师的单元测试?

单元测试需要由开发人员进行编写,用来检验我们写的代码是否正确,属于白盒测试。
单元测试通常是判断某个类中的某个方法返回的结果是否符合预期,是否抛出异常等等。
单元测试是一种保障,保障我们的代码与预期是一致的正确的,有保障了是不是会安全感倍增呢?
在标准的开发过程中,单元测试的代码与实际程序的代码具有同等的重要性。

这是一种前驱式的测试方式,因为代码写完之后,还没有提交到测试环境,
此时开发人员可以通过写测试用例的方式进行自测,
以免发布到测试环境之后,被别人测出来有很严重的问题,让自己很没面子。

程序员有责任编写功能代码,同时也就有责任为自己的代码编写单元测试代码。
执行单元测试,就是为了证明这段代码的行为和我们期望的一致。

做好了单元测试可以缩短开发周期,提高代码质量,提高项目的稳定性。
编写单元测试代码明明是会增加工作量,为什么还能缩短开发周期呢?
因为一旦发现程序中出现问题,就需要程序员去fix掉这些问题,
在不断的fix与不断的check的过程中,其实这就是延长开发周期了。

如果开发人员要花大量时间去解决项目后期发现的越来越多的令人头疼的八阿哥(BUG),
那其实是很得不偿失的,谁也不能保证谁的代码一定没有问题,包括我在内。
所谓测试不是用来证明你是对的,而是用来证明你没有错。

当然了,我们也可以不写单元测试来达到测试的目的,就是所谓的人肉测试,
我们可以通过自己在main()中写一些代码来调用待测试方法,自己手动去执行每个main(),
用眼睛去看结果是否出现问题,也可以把服务全部跑起来,然后通过ui的方式,
用手去点击相关功能,并用眼睛去看是否出现问题。

在人肉测试的方法中,我们的眼睛和手占了很大的比重,如果测试点很少,那这么玩绝对没有问题,
但请试想一下,经过日积月累后,现在项目中有1000个测试点,
每次发版&上线前都需要去确认这些测试点是准确无误的,那你的眼睛和手还够用吗?
就算够用,时间一长,你也不屑于每次都要做这种简单无趣的劳动。


一些现实中的例子
在现实中遇到了痛点,才会真正的去思考,事情都是分两方面的,坏的一面是让我们走弯路,碰壁。
好的一面是总结后会让我们有所思考,有所成长。


例子1(测试前置步骤多,浪费时间)
java代码写好后,在提交给测试工程师前需要自己先测试一下,看看是不是有什么bug,
千万不要被测试工程师发现什么很low的问题,这样自己太没有面子了。。。
我们先看没有单元测试用例的情况下,你需要编译、打包、启动项目(假设你是个javaweb项目),
然后在浏览器输入localhost:8080/xxx/xxx

一顿折腾后终于能进入相关的页面,哎呀,忘了还需要登录一下,帐号密码没有?
没关系,我们可以注册嘛。一顿折腾后终于能登录了,哎呀,
忘了要测我这个功能的用户必须要进后台管理平台去配置相关的权限,
但是后台管理平台的服务还木有启动,没关系,我们可以再启动嘛。

一顿折腾后权限配好了,哎呀,忘了还要求当前的帐号需要走一长串工作流的审批,
才会分配相应的资源,没关系,我们可以手动去点击然后走这个工作流嘛。
(。。。省略各种折腾。。。。 )

yes,把这些测试的前置步骤都完成了,下面终于激动的要开始在页面上测试自己的功能了,
兴奋的点击了一下,后台抛了一个异常出来,貌似这是一个bug。。。

没关系,加断点调试,定位问题,哎呀,真的是一个bug,好吧,改代码吧,改好之后。。。
注意了,改好之后怎么办?
您又要编译、打包、启动项目、打开浏览器输入url、登录、去查看那个刚才的那个功能。。。

很激动,这次妥妥的,肯定没问题了,然后一点击:
哎呀,又出异常了。。。查了半天,突然发现是改错了,然后又改好了,这次妥妥的,准没问题了。
您又要编译、打包、启动项目、打开浏览器输入url、登录、去查看那个刚才的那个功能。。。

看到痛点了吗?
我们为了测试自己写的一个功能,要做多少前置步骤?而一旦发现代码有bug,
改好之后又要重复若干的前置步骤,浪费了大量的时间与精力,
为什么有经验的程序员效率高,代码质量高,工资高,这其实是工作方法的问题。

我们是否可以通过写单元测试用例的方式,把问题暴露在没有这一堆前置步骤的时候呢?
这样会节省我们大量的前置步骤时间。


例子2(项目上线自动化回归测试)
现在项目里有100个功能,这回我们新做了一个功能,新功能测试通过了,然后欢欢喜喜的去上线,
发现新功能改了一个通用方法,导致老功能出现了一个严重的bug,倘若这个老功能是核心功能,
那这就是严重的线上事故,如果没有单元测试用例做保障,那这种线上事故将成为可能。

怎么防范这种事故的出现?每次发布前都要手动测试一遍核心的功能?这样会不会累死了?
我们需要自动化来完成这件事情,结合jenkins+maven+junit。


例子3(修复完bug后,补上测试用例,以防后患)
生产环境出现bug的时候,救火队员火急火燎的去分析问题,解决问题,最终发布之后呢?
除了解决问题之外,还要思考是否能通过增加某些测试用例的情况来杜绝此类问题再次发生,
自己横向思考一下。亡羊补牢为时不晚,如果没有最后这一步的思考,
那下次是不是此类问题还会出现呢?

分析
如果大家分析一下我们bug原因的构成,
一部分bug的原因是在编码时没考虑到某些case或者边际条件。
造成这种问题的原因很多,其中很重要的一个原因是我们对工作代码所要完成的功能思考不足,
而编写单元测试,特别是先写单元测试再写工作代码,
就可以帮助开发人员思考编写的代码到底要实现哪些功能,类似于测试驱动开发的感觉。

编写单元测试代码的过程就是促使开发人员思考代码实现的过程,
之后实现代码的时候,开发人员思路会更清晰,实现代码的质量也会有相应的提升。
发现问题早发现早治疗,可以避免很多低级错误。


TDD测试驱动开发
“大军未动,粮草先行”,喜欢三国演义的朋友都比较熟悉。
“代码未动,测试先行”,喜欢极限编程的朋友都比较熟悉。

在没有开始编写自己的业务代码之前,先把测试用例写好,再补全业务代码,
使用用测试用例来驱动开发业务代码。

我们在设计测试用例的时候,其实也是在设计我们业务代码的过程,
我们要去预先思考,我们的业务代码要处理哪些问题,哪些边界条件,哪些流程等等。
这个过程会使得我们的流程更加有保障,会让我们的业务代码更具有条理性,安全性。

在业务代码没有编写的情况下,就编写测试用例,怎么写啊?写完不是都会报错吗?
没错,报错就对了,因为我们的业务代码还没有编写,此时再去补全业务代码即可,
先补全到不报错就可以了。

TDD步骤
1.思考一下这个测试用例该如何编写,我们的业务类需要提供哪些功能,方法提供哪些功能?
其实这是一个设计的过程,让我们可以站在使用者的角度去思考和观察问题,
驱动我们把代码设计成可测试的&松耦合的,设计完成之后先编写测试用例。

编写好测试用例之后,会报很多红叉子(编译错误),这是正常的,
因为我们的被测试类与方法此时还没有建立。
此时再去编写测试用例要测试的业务类与方法(测试用例写在被测试类与方法的前面),
之后再去run这些用例,肯定是失败的,因为被测试的方法里还什么都没有。
第一步就完成了,截止到让测试用例执行的时候都是失败就可以了。

2.编写代码让测试用例执行成功&通过,只要通过测试用例即可,不用考虑其他。

3.重构代码,继续让测试用例继续保持通过,此时可以大胆的重构,
因为已经有若干个测试用例做保障,如果重构出了问题也不用担心,因为测试用例会报错,
开发者可以在第一时间知道错误。

总结:使用TDD,使我们换位思考,转换到使用者的角度去设计程序,开发程序,
使程序天然是可测试性的,提升了安全性。

PS:对极限编程感兴趣的童鞋可以延伸搜一下结对编程。


单元测试的优势

减少人肉测试,为自动化测试打基础
快速保障业务代码的正确性,减少人肉测试,减少人工投入成本,整体上增加工作效率。
加上各种测试工具配合使用,可以形成可视化的测试报告以及测试覆盖率文档,让测试指标可量化。


让代码维护更容易
所谓维护性,就是别人看到你写的代码,能不能很快的看懂并修改你的代码。
如果你写的代码像一坨。。。那么无形中会给其他同事修改你的代码造成了巨大的压力与屏障。

由于给代码写很多单元测试,相当于给代码加上了规格说明书,
开发人员通过读单元测试代码也能够帮助开发人员理解现有代码,
能够向其他程序员展现你的程序该如何调用。
很多开源项目都有相当量的单元测试代码,通过读这些测试代码会有助于理解生产源代码。


提升反馈速度,减少重复工作,提高开发效率
开发人员实现某个功能或者修补了某个bug,如果有相应的单元测试支持的话,
开发人员可以马上通过运行单元测试来验证之前完成的代码是否正确,
而不需要反复通过发布war包、启动容器、通过浏览器输入数据等繁琐的步骤来验证所完成的功能。
不会浪费时间,而相反是节省时间。


放心大胆的去修改老代码、去重构、去改进质量
保证你最后的代码修改不会破坏之前代码的功能。项目越做越大,代码越来越多,
特别涉及到一些公用接口之类的代码或是底层的基础库,
谁也不敢保证这次修改的代码不会破坏之前的功能,
所以与此相关的需求会被搁置或推迟,由于不敢改进代码,代码也变得越来越难以维护,
质量也越来越差,因为害怕牵一发而动全身,导致代码越来越烂。

由于代码的历史功能都有相应的单元测试保证,相当于有了一层防护网,修改了某些代码以后,
通过运行相关的单元测试就可以验证出新调整的功能是否有影响到之前的功能。
当然要实现到这种程度需要很大的付出,不但要能够达到比较高的测试覆盖率,
而且单元测试代码的编写质量也要有保证。


允许只验证部分功能
当一个业务逻辑包含了若干个service方法时,
我只想单独跑其中的一个service方法的测试用例成为可能。
消除外部依赖,只关注单独的一个service方法。


单元测试的劣势

学习成本
如果只是单纯的使用Junit或是TestNG这样的基础单元测试框架往往无法应对复杂的单元测试情况,
所以势必要借助很多第三方的框架和技术(easymock,jmock,jenkins,maven&gradle等等),
这些框架和技术的学习还是会增加学习的成本和难度。
但其实学习这些知识点也是一个程序员成长的过程,所以说还好。


增加工作量
单元测试跟生产代码是一样的,并不会因为是用来测试的就有所不同,
开发人员同样要面对测试代码的编写、维护等工作,也同样要面对避免重复代码等一系列问题,
能否写出好的测试代码还是取决于开发人员的设计和编码能力。
写测试代码是需要花时间和精力的,因此会增加一部分工作量。


推广和运用单元测试需要比较大的投入
只有在每个开发人员都编写了足够的、质量好的单元测试代码,
大家才能真正享受到单元测试带给我们的好处。
在达到这种层度以前,还需要不少时间和资源的投入。


Junit4测试框架
上面的话术都是理论派,那我们下面就需要实际的来写一些单元测试的代码了,用什么写呢?
首选是用Junit,Junit是一个测试框架,类似的框架还有类似于TestNG等等,
Junit可以用来编写单元测试用例,并可以自动化的去运行测试用例,以减少之前人肉测试的痛点,
使用Junit能够帮助我们减少在开发过程中的错误,把Bug扼杀在萌芽之中,
有利于代码的后期维护和检查。

使用jenkins+maven+junit,让成建制的自动化执行测试用例变成可能。
使用eclipse等IDE或者使用maven插件生成测试报告,可以直观的看到测试结果,
一般使用红色表示执行失败,用绿色表示执行成功。

Junit4常见注解
注解名称
注解描述
@Test
所有希望被运行的测试方法都应该使用该注解,
次注解来告诉junit框架,当前方法是需要被执行的测试用例。
@Test
(expected=NullPointerException.class) 
期待抛出指定异常才会通过,注解中定义的异常可以是
父类异常,方法中抛的异常只要是其子类就可以。

如果没有此注解还想要测试抛异常的情况就必须要
自己手动写try-catch,这样就麻烦了。
@Test(timeout=5000)
执行超出时间无法通过(单位毫秒),
对性能&执行时间比较苛刻的测试方法可以使用此注解。
@Ignore("暂不执行,因为xxxx,skipped")
忽略当前测试用例,需要写在@Test上面。
有些用例没写完或者不想在自动化跑测试用例的时候执行,
那我们就需要ignore这些用例。
@Before
使用@Before来修饰单个非测试方法,
其他每个测试方法执行之前都要执行@Before修饰的方法。
可以用来创建一些测试数据&执行一些sql等初始化操作。 
无论用例成功or失败都会去执行@Before修饰的方法。
@After
使用@After来修饰单个非测试方法,
其他每个测试方法执行之后都要执行@After修饰的方法。

可以用来关闭&回收&清理一些资源的操作。
无论用例成功or失败都会去执行@After修饰的方法。
@BeforeClass&@AfterClass
使用@BeforeClass来修饰单个非测试方法,
使用@AfterClass来修饰单个非测试方法。

所有测试方法执行之前要执行@BeforeClass修饰的方法。
所有测试方法执行完毕之后要执行@AfterClass修饰的方法。
类比一下,@Before与@After会执行多次,
测试类中有多少个测试方法就会执行多少次,
而@BeforeClass与@AfterClass只会执行一次。
@FixMethodOrder
(MethodSorters.NAME_ASCENDING)
junit测试用例的执行默认是没有顺序的,
使用此标签可以设定执行顺序,此处为按文字顺序执行,
修饰在类上。


Assert断言
如果没有断言,我们一般会在测试方法中使用System.out.println()来将返回值打印到控制台,
然后用眼睛去看,这还是所谓的人肉测试,那如果有成百个测试方法,那眼睛岂不是要累死了。

除了在测试方法中使用输出语句之外,我们还可以使用断言,来判断程序是否符合某个条件,
让Junit自动去执行去比较期待值与实际值(被测试方法的返回值)是否一致,
而不用肉眼观察的方式来完成。

断言为自动化测试打下了一个良好的基础。如果实际值与期待值一致,则测试通过,
否则测试失败。如果有可视化界面的话会发现测试通过为绿色状态,测试失败为红色状态。

断言在org.junit.Assert类中,有一组以assert开头的方法用于断言测试,基本上涵盖了大部分需求。
下面列举几个常用的,如果有需要的话可以直接调用assertFail方法让断言直接失败。
断言方法
描述
Assert.assertEquals(a,b);
判断a与b是否相等,如果相等则表示测试成功。
如果不相等则表示测试失败并抛出Error。
Assert.assertNotEquals(a,b);
判断a与b是否不相等,如果不相等则表示测试成功。
如果相等则表示测试失败并抛出Error。
Assert.assertTrue(a);
判断a是否为true,如果为true则表示测试成功。
如果a不为true则表示测试失败并抛出Error。
Assert.assertFalse(a);
判断a是否为false,如果为false则表示测试成功。
如果a不为false则表示测试失败并抛出Error。
Assert.assertNull(a);
判断a是否为null,如果为null则表示测试成功。
如果a不为null则表示测试失败并抛出Error。
Assert.assertNotNull(a);
判断a是否为非null,如果为非null则表示测试成功。
如果a不为非null则表示测试失败并抛出Error。
Assert.assertFail();
这个标签的使用是有技巧的,
如果测试方法中写的代码全部被成功执行并走到了最后,
但期待的是走到最后就算是测试失败,测试成功则不应该走到最后,
那么就要使用到此标签。
Assert.assertArrayEquals(a,b);
判断数组a与数组b是否相等,如果相等则表示测试成功。
如果不相等则表示测试失败并抛出Error。


Runner运行器
junit框架使用runner来调用并运行&执行我们的单元测试用例,junit中存在多个不同的runner,
每个runner都会有特殊的功能,我们根据需求来使用不同的runner。
如果没有指定特定的runner,则junit会使用TestClassRunner.class作为默认runner。
我们通过@RunWith(TestClassRunner.class)来指定一个runner,需要修饰在类上。
只要对一个类指定了runner,那么这个类中的所有方法都被这个runner来调用。
在写测试用例的过程中,我们经常需要获取spring容器中的bean对象来测试业务逻辑,
那么我们就需要一个可以自动获取spring容器的runner:SpringJUnit4ClassRunner。

结合spring的junit4例子
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/app-config.xml")
@Transactional // 事务自动回滚
public class AATest {
  @Resource
  IndexService indexService;

  /**
   * Title: 注册测试用例正确.<br>
   * Description: registerTest<br>
   * CreateDate: 2017年6月19日 下午4:36:00<br>
   *
   * @category registerTest
   */
  @Test
  public void studentRegisterTrueTest() throws Exception {
    final UserRegisterValidationForm paramForm = new UserRegisterValidationForm();
    paramForm.setPhone("1706868699");
    paramForm.setPwd(SHAUtil.encode("caowei123"));

    final CommonJsonObject<User> json = indexService.studentRegister(paramForm, null);
    Assert.assertTrue(json.getCode() == 200);
  }


Suite打包测试
在一个正儿八经的项目中,只写一个测试类是不可能的,我们通常会写出很多很多个测试类。
可是这些测试类必须一个一个的执行,也是比较麻烦的事情,还是相当于半个人肉测试。

因此Junit为我们提供了打包测试的功能,将所有需要运行的测试类集中起来,
一次性的运行完毕,大大的方便了我们的测试工作。

当然我们还可以打造自己的测试树形结构,Suite是可以嵌套的,一个父Suite中包含了多个子Suite,
子Suite中包含了若干个测试类,每个测试类中又包含了若干个测试用例方法。

Suite允许我们自己灵活的来管理测试力度,这次我想把所有的用例都跑一遍,执行父Suite就可以,
下次我想只跑某个模块下的测试用例,执行某个子Suite就可以。

具体代码如下:
import org.junit.runner.RunWith;
import org.junit.runners.Suite;

@RunWith(Suite.class)
@Suite.SuiteClasses({ATest.class, BTest.class})
public class AllTests {

}
这里需要使用Suite.class作为Runner,在@Suite.SuiteClasses中需要写上需要聚合的测试类。
PS:如果项目中使用到了maven,maven会自动运行所有的测试用例,可以代替suite功能。


参数化测试
我们经常会给待测试方法传入不同的参数来检验被测试方法是否达到预期效果,例如边界测试等等。
参数的不同就会导致要写多个不同的测试用例方法,那这样岂不是很麻烦?
我们可以使用参数化测试,只写一个测试用例方法即可,把不同的参数一次性传递给被测试方法。
@RunWith(Parameterized.class)
public class Junit4Test2 {
	private int param;
	private int result;

	public Junit4Test2(int param, int result) {
	    this.param = param;
	    this.result = result;
	}

	@Parameters
	public static Collection data() {
		return Arrays.asList(new Object[][] { { 1, 2 }, { 2, 3 }, { 4, 5 }, });

	}

	@Test()
	public void	 test1() {
		//使用参数化测试
		Assert.assertEquals(result, JunitDemo.demo(param));
	}

	@Test()
	public void test2() {
		//未使用参数化测试
		Assert.assertEquals(2, JunitDemo.demo(1));
	}
}

@RunWith(Parameterized.class)这条语句就是为这个类指定了一个参数化的Runner。
如果是带Spring的测试用例就不能使用这种方式,@RunWith(SpringJUnit4ClassRunner.class)。
每个测试类只能使用一个@RunWith,因此Sutie与Parameterized的用武之地就不是那么的大了。


Junit4注意事项
~测试用例类使用public修饰;
~测试用例的测试方法使用public修饰;
~测试用例的测试方法的返回值应该是void;
~测试用例中测试方法不应该有任何参数;
~在Eclipse中用鼠标选中一个测试方法点击run或debug则会只执行当前的测试方法,
否则会执行测试类中所有的测试方法。


注意事项&经验总结

~尽可能的开发出可测试性的代码,这样更容易提升代码的健壮性、安全性。
在编写代码的同时也要去思考,如何能让这些代码可以被测试,或者直接使用TDD。

~查询条件带时间的,尤其是要拿当前时间的地方,需要通过参数传进去,
不然,没法写测试用例(写完测试用例还要造实时数据比较麻烦)。

~不要把类似于HttpServletRequest、HttpServletResponse这种对象传递进service方法,
编写单元测试的时候,不好模拟这种对象,并且这些对象属于web层,不应该进入到service层。

~不要使用System.out.print来写测试用例,需要使用Assert类来进行判断,否则还是人肉测试。

~尽可能的自动化,自动化回归测试+jenkins+maven+junit自动跑所有的单元测试用例+api测试工具,
把单元测试用例的威力彰显出来,单元测试+持续集成+自动化才是王道。

~测试覆盖率,单元测试覆盖率查看可以使用maven的插件来完成,还有测试报告,非常直观。

~不要把测试用例玩成形式主义。 在真正测试之前,就先要跑测试用例,看测试用例能不能通过。  
养成习惯:给别人调用之前,先跑一下自己的单元测试用例,看能不能跑过?

~在维护&重构代码的同时也要去维护&重构测试用例代码,否则可能会造成编译错误或运行错误。

~线上出现问题,解决完毕后,去思考是否对这个case可以用增加测试用例的方式来提升质量。

~不要把单元测试设计的非常复杂,要保证可读性,简单性。

~单元测试不能依赖时间与空间的变化而导致不同的执行结果,保证无论何时何地执行结果都一致。
例如:早上7点执行与晚上7点执行的结果不同,使用A机器与B机器执行的结果不同等等。

~service方法的insert&update方法需要传一个boolean或int回来,而非void。
因为个别情况会导致数据库保存失败但没有报错,程序认为执行成功的窘境。

~需要有专门的数据库留给跑测试用例,在构建测试环境时自动化运行单元测试用例。

~在代码评审&review的时候,别忘了对单元测试用例也进行一下评审&review。

~核心重要功能,需要多个正确错误的用例。边角料功能可不加测试用例。
我们在写测试用例的时候,大多数只写逻辑正确时的测试用例,但却忽略了逻辑错误时的测试用例,
正常情况下, 错误的测试用例也非常重要,例如:如果输入5,方法会抛异常,
但输入小于5的数就正常,此时我们应该至少有一个用例要写成5,我们要尝试,
给程序错误参数的时候,程序是否会信守承诺的抛出异常,如果能抛出异常,
代表我们的程序没有问题,是比较健壮的。相反如果没有写错误的测试用例,则健壮性降低,
会有出现bug的可能,就是输入5也没有抛异常,这就是bug了。


controller层方法是否要写junit单元测试?
正方:写,如果controller层与service层都是一个人写的那确实有些重复,但对于跨团队作战,
尤其是分布式架构,分布式团队,就需要写,这样质量更高。
可以使用类似于easymock等框架来模拟HttpServletRequest对象进行单元测试。

反方:不写,controller方法的逻辑较少,一般只是数据封装以及参数检查,没有必要写,
并且controller层的测试用例与service层的测试用例会有重复的情况,浪费工作量。

个人观点:无论写与不写都需要有自动化API测试工具跟着,
例如jenkins+newman、postman、jmeter等,controller层的单元测试用例的优先级没有service层高。


总结
写代码是需要不断的进行重构的,因为谁也没有把握能一次性就写出非常优雅毫无破绽的代码,
更何况随着访问量、代码量的激增,更加的需要通过重构来保障代码健康、稳定的运行。
那单元测试用例,就好比是一道安全网,在你重构出错的时候,安全网会第一时间知道,
会第一时间通知你,改错了,这其实是一种前趋式的解决潜在bug,把bug扼杀在摇篮中。
有了安全网,就可以放心大胆地去重构我们的代码。

综上所述,其实单元测试的重要性很简单,
不写单元测试,你怎么知道你的代码写的对不对?
没有足够丰富的测试用例,你怎么知道用户会怎么使用到你的代码,
你又怎么会知道你的代码应该怎么被执行呢?
所以,单元测试很重要。和写代码一样重要。所谓的:无单测,不编码!

个人建议,如果是核心&重要的功能,不仅必须要写测试用例还必须要写多个用例,
正确的用例,错误的用例都要写,至于简单&非核心业务,用例可以适当的省略,
并且测试用例覆盖率也是一个可前移的量化指标,最终我们是通过测试用例这种手段,
来保证我们代码的质量,没有质量,再谈其他都变得没有意义了。

主要是看你的付出产出比是否值当,值就写,不值就不写,至于度,自己拿捏。

猜你喜欢

转载自blog.csdn.net/piantoutongyang/article/details/80262443