当前位置: 首页 > 后端技术 > Java

测试用例一定不能随便,记录一个测试用例异常引起的思考

时间:2023-04-01 17:42:29 Java

一个测试用例,你平时写还是不写?我以前只写业务接口的测试用例,每个接口一个,只写一种数据用例。只要能够跑过去。如果您想要不同的场景,请更改数据。再次运行它。简单易行。但是自从业余时间开始维护开源后,加深了对测试用例的理解。即使是现在,我也已经将测试用例的地位提高到与核心代码同等重要的地位。我曾经开玩笑说,只写核心代码不写测试用例代码的都是流氓。开源项目是给大家看的,每个公司环境不一样,项目结构也不一样,jdk、spring系统版本、第三方依赖包都不一样。所以开源框架必须在所有场景下都能正常工作。这么多的功能,这么多的场景,就算我是作者,光靠熟悉也不可能记住这么多细节。这时候测试用例就非常重要了,是整个项目最关键的质量保证。很多时候,我依靠测试用例来发现一些边际错误。目前我的开源项目有870个测试用例,覆盖了90%以上的场景。本文探讨了一个关于测试用例引起的测试用例执行机制的问题。第二件事的起因是群里有个小伙伴发现,当某个单元测试用例配置项不正确时,springcontext执行了两次,但是在配置正确的情况下,一般只启动一次。这让他不解,以为是画框出了问题。之所以认为是spring启动了两次,是因为他看到日志中打印了两次springboot的标识,报了两次同样的错误:。_________/\\/___'_____(_)______\\\(()\___|'_|'_||'_\/_`|\\\\\/___)||_)||||||(_||)))'|____|.__|_||_|_||_\__,|////=========|_|==============|___/=/_/_/_/::SpringBoot::(v2.0.5.RELEASE)com.yomahub.liteflow.exception.ELParseException:Programerror,doesnotmeetthegrammarspecification,nomatch适当的语法,最大匹配[0:7]atcom.yomahub.liteflow.builder.el.LiteFlowChainELBuilder.setEL(LiteFlowChainELBuilder.java:124)~[liteflow-core-2.8.2.jar:na]在com.yomahub.liteflow.parser.helper.ParserHelper.parseOneChainEl(ParserHelper.java:391)~[liteflow-core-2.8.2.jar:na]在com.yomahub.liteflow.parser.el.XmlFlowELParser.parseOneChain(XmlFlowELParser.java:20)~[liteflow-core-2.8.2.jar:na]在java.util.ArrayList.forEach(ArrayList.java:1259)~[na:1.8.0_292]在com.yomahub.liteflow.parser.helper.ParserHelper.parseDocument(ParserHelper.java:217)~[liteflow-core-2.8.2.jar:na]在com.yomahub.liteflow.parser.base.BaseXmlFlowParser.parse(BaseXmlFlowParser.java:40)~[liteflow-core-2.8.2.jar:na]]。________/\\/___'_____(_)____\\\(()\___|'_|'_||'_\/_`|\\\\\/___)||_)|||||||(_||)))'|____|.__|_||_|_||_\__,|////=========|_|==============|___/=/_/_/_/::SpringBoot::(v2.0.5.RELEASE)com.yomahub.liteflow.exception.ELParseException:程序错误,不符合语法规范,没有匹配到合适的语法,com.yomahub.liteflow.builder.el最大匹配为[0:7]。LiteFlowChainELBuilder.setEL(LiteFlowChainELBuilder.java:124)~[liteflow-core-2.8.2.jar:na]在com.yomahub.liteflow.parser.helper.ParserHelper.parseOneChainEl(ParserHelper.java:391)~[liteflow-core-2.8.2.jar:na]在com.yomahub.liteflow.parser.el.XmlFlowELParser.parseOneChain(XmlFlowELParser.java:20)~[liteflow-core-2.8.2.jar:na]在java.util.ArrayList.forEach(ArrayList.java:1259)~[na:1.8.0_292]在com.yomahub.liteflow.parser.helper.ParserHelper.parseDocument(ParserHelper.java:217)~[liteflow-core-2.8.2.jar:na]在com.yomahub.liteflow.parser。base.BaseXmlFlowParser.parse(BaseXmlFlowParser.java:40)~[liteflow-core-2.8.2.jar:na]测试用例代码为:@RunWith(SpringRunner.class)@TestPropertySource(value="classpath:/whenTimeOut/application1.properties")@SpringBootTest(classes=WhenTimeOutELSpringbootTestCase.class)@EnableAutoConfiguration@ComponentScan({"com.yomahub.liteflow.test.whenTimeOut.cmp"})publicclassWhenTimeOutELSpringbootTestCase{@ResourceprivateFlowExecutorflowExecutor;//其中b和c在when的情况下超时,所以抛出了WhenTimeoutException的错误@TestpublicvoidtestWhenTimeOut1()throwsException{Assert.assertFalse(response.isSuccess());Assert.assertEquals(WhenTimeoutException.class,response.getCause().getClass());}}开源框架都是源码级别的,不可能再主动启动springcontext(其实我想启动也不知道怎么做)而且如果是也正常它配置正确。而spring的@Configuration也启动了两次。从线程栈来看,这里也是Junit触发的:值得一提的是报错是在springboot启动环节。所以根本没有进入@Test修改的测试用例代码。所以它与代码编写的内容无关。我测试了一下,测试代码如果抛出异常,spring上下文只启动一次。所以这个问题可能到这里就结束了,因为不是框架本身的问题。Junit本身在启动spring失败时触发了2个动作来初始化spring,这可能是Junit的一种重试机制。这不是我能控制的。反正真有错误就抛出来,不需要细心初始化几次,也不会影响我测试用例的整体效果。只需更正特定的测试用例。三但是在处理一个测试用例的时候突然想到了测试用例的Spring加载机制,让我想起了之前的问题。我突然恍然大悟。我们用例的结构一般是一个测试用例代表一个大的场景,其中的每一个方法代表一个具体的用例。假设一个类有10个测试特定用例,当您在该类上单击RunTest时,spring将被初始化多少次。答案是1次。springboot测试为了加快测试用例的运行速度,不可能对每个方法都初始化spring。该类中的springcontext会被缓存,这10个方法会共享同一个springcontext。具体运行机制是:点击类的RunTest时,会先初始化spring,然后开始运行各个测试方法。测试方法运行时,如果发现spring没有初始化,会重新初始化spring。这就说明当我们单独运行方法的run测试时,spring也会被初始化。现在前面的问题就可以解释了,因为初始化失败了,运行方法的时候发现还没有初始化,于是重新初始化。但是对于不同的Test类,还是会被多次初始化。也就是说,每个类都会重新初始化spring。当您运行多个测试用例时,应该注意这一点。四、扩展一个附加问题:有没有人遇到过运行所有测试用例时,总会有几个总是报错,而单次运行却完全正常的问题?如果你遇到过,你肯定忽略了以下一点:如果选择运行所有测试用例,虽然每个测试用例类都初始化了一次spring,但是JVM自始至终只启动了一次。而你在类中定义的静态变量不会随着spring启动而改变。当你运行所有的东西时,你的错误测试用例引用的一些静态变量可能仍然是前一个测试用例遗留下来的数据。所以它可能是错误的。但是单次运行时,就没有这种现象了。如果遇到这种情况,就得在测试用例中使用@AfterClass注解,在注解声明的方法中清除测试用例中的静态变量。这样你就可以一起运行它们。比如我的每一个测试用例都继承了一个BaseTest方法,在里面写了这个方法来清除静态缓存:publicclassBaseTest{@AfterClasspublicstaticvoidcleanScanCache(){FlowBus.cleanCache();ExecutorHelper.loadInstance().clearExecutorServiceMap();SpiFactoryCleaner.clean();LiteflowConfigGetter.clean();}}五、测试用例怎么写,常用的写法有哪些。我不会在这里做太多的解释。百度一下应该可以找到很多教程,或者有兴趣的也可以看看我的开源项目LiteFlow中的测试用例。测试用例除了可以保证你的项目质量之外,还可以清楚的看到你的整个测试用例覆盖了多少行代码。这里的测试用例是在单独的项目中编写的。用于区分核心工程包。然后在IDEA中配置单独执行测试用例的任务:然后点击runxxxwithcoverage按钮运行测试用例:多个测试项目之间,运行一个后,会弹出对话框询问是否要添加的结果这次到总的结果中去,直接点击添加:你所有的测试用例项目都运行良好,右侧会显示一个报告页面如下:我的整个测试用例是79%。但这并不意味着该项目只覆盖了79%的场景。线路覆盖和功能场景覆盖是两个概念。这里只是表示所有的测试用例都跑完了,所有代码行的比例都跑完了。最后希望大家千万不要忽视测试用例。虽然有时候写的时候想吐,但最后你会体会到其中的甜头。