每次跑 UI 自动化测试的时候开始都会重复启动 app 两次,之后才开始跑测试用例,因为也不影响用例的正常运行,所以这个问题发生了一个月还处于被搁置的状态。
直到今天,我突然发现不对劲,android 测试用例运行也会出现这样的情况(我之前以为 ios 端运行就是会那样),之前 android 是不存在这个问题的,让我意识到事情并不简单。
因为是通过 appium 开启 session 测试会话,和设备上的 app 进行交互的也是它,首当其冲的选择先看它的运行日志,果不其然,发现一次运行莫名其妙地重复开启了两次回话。
既然问题的根源已经找到,那么回到程序,为什么会重复开启两次回话?我在设置程序中涉及到初始化 session 会话的代码处打了断点,发现重复运行了两次。
继续追本溯源,查看为何会初始化 appium driver 的代码会被调用两遍,最后发现问题所在,原因如下。
// 这个是所有的测试用例继承的父类
public class CommonCaseBase extends TestCaseBase {
private static final Logger LOG = LoggerFactory.getLogger(CommonCaseBase.class);
Configuration configuration = new Configuration();
public CommonCaseBase() {
}
@BeforeSuite
public void onStart() throws InitEnvException {
LOG.info("单个套件测试开始,初始化appium driver");
Configuration conf = new Configuration();
DriverManager.initDriver(conf);
PageFactory.initPageFactory(conf);
}
2.通过上述方式我能保证单个测试 Suite 下的所有测试用例类运行时只进行一次初始化
<suite name="EhiDriverPadTest">
<test verbose="2" name="testA">
<classes>
<class name="cn.ehi.testcase.base.DataSetUp"/>
<class name="cn.ehi.testcase.base.UserLogin"/>
<class name="cn.ehi.testcase.CloseOrderQuicklyTest"/>
<class name="cn.ehi.testcase.SingleOrderTest"/>
<class name="cn.ehi.testcase.MutilOrderTest"/>
<class name="cn.ehi.testcase.CrashClickTest"/>
<class name="cn.ehi.testcase.base.DataReset"/>
</classes>
</test>
</suite>
3.然而出现的漏网之鱼,我发现并不是所有的测试用例都继承了 CommonCaseBase 父类(这个是我前段时间重构代码时所导致的 bug)
而 AndroidCaseBase 这个类的功能和 CommonCaseBase 大体相同,也会上述的初始化操作,这就导致同通个测试 Suite 运行时会触发两次 BeforeSuite 的初始化。因为它们存在于不同的父类之中,所以 testng 认为它们是两次不同的初始化操作,故调用两次,当然这个只是我的猜想。
1.照常使用 debug 大法,可以看到最后运行时 beforeSuiteMethods 确实存在两个
2.继续探索 testng 扫描方法的机制
可以看到在初始化 TestngRunner 对象的过程中,会对所有需要被调用的方法进行收集
首先获取测试 Suite 中包含的所有类,并且封装成 testng 定义的 ITestClass
继续观察 TestClass 的初始化过程,会发现其中有一个 initMethods() 方法,扫描逻辑都藏在里面
中间的过程就不说了,反正就是一通调用,最后我们来看到最后这个扫描方法的逻辑
先获取到测试类中声明的所有方法,包含父类,循环向上获取父类方法(直到 Object 类)
之后便对收集到的所有方法列表进行注解扫描,其中就有包含 BeforeSuite 注解的获取
80% 的 bug 都是因为你程序编写方面存在问题,不要时常想着是框架的锅,是工具的错误,其实很多时候都是你的用法上存在错误,本次通过阅读 Testng 的源码,对测试用例初始化这一块有了进一步的了解,后续也会多注意这方面的坑。