【发布时间】:2011-02-11 13:29:58
【问题描述】:
我一定在这里遗漏了什么。 ActivityUnitTestCase 的 JavaDoc 建议这个测试用例测试一个与系统隔离的 Activity:
此类提供对单个活动的独立测试。测试中的 Activity 将在与系统基础架构的最小连接的情况下创建,您可以注入许多 Activity 依赖项的模拟或包装版本。
我假设这包括不实际启动应用程序。此外,它还公开了一个 setApplication 帮助器,可以用来注入模拟应用程序。
但是,任何ActivityUnitTestCase 我开始启动(实际)应用程序并调用其onCreate 方法。更准确地说,InstrumentationTestRunner 似乎正在这样做,甚至在我有机会在我的测试的 setUp 方法中使用 setApplication 之前就这样做了!很长一段时间我都没有注意到这一点,因为它似乎发生在测试套件启动期间甚至没有到达 Eclipse 断点的某个时间点,但是写入 onCreate 中的日志显示它实际上已被调用。
这完全超出了我的范围。当 Android 的测试运行程序实例化并执行实际应用程序时,为什么我要使用模拟应用程序对象?考虑到检测运行器在其自己的线程中运行,并且在这样做时会产生主应用程序线程,这将更加成问题。这意味着正在执行的测试和调用Application.onCreate 之间存在竞争条件。如果您在那里做了任何可能影响您的测试的事情,例如写入共享首选项文件,那么你就完蛋了,因为你的测试会随机失败。
我是否遗漏了什么,或者这只是测试框架中的严重疏忽?
更新
这似乎也会影响ApplicationTestCase。在我的测试用例开始之前,我可以在我的应用程序类'onCreate 中到达一个断点。我们在那里启动了一个即发即弃的 AsyncTask,它会随机失败,因为我没有机会模拟它(记住,那是在我的测试用例调用 setUp 之前)。这是我在 onCreate 这个晦涩的调用期间看到的堆栈跟踪:
Thread [<1> main] (Suspended (breakpoint at line 86 in QypeRadar))
QypeRadar.onCreate() line: 86
InstrumentationTestRunner(Instrumentation).callApplicationOnCreate(Application) line: 969
ActivityThread.handleBindApplication(ActivityThread$AppBindData) line: 4244
ActivityThread.access$3000(ActivityThread, ActivityThread$AppBindData) line: 125
ActivityThread$H.handleMessage(Message) line: 2071
ActivityThread$H(Handler).dispatchMessage(Message) line: 99
Looper.loop() line: 123
ActivityThread.main(String[]) line: 4627
Method.invokeNative(Object, Object[], Class, Class[], Class, int, boolean) line: not available [native method]
Method.invoke(Object, Object...) line: 521
ZygoteInit$MethodAndArgsCaller.run() line: 868
ZygoteInit.main(String[]) line: 626
NativeStart.main(String[]) line: not available [native method]
为什么测试运行者callApplicationOnCreate 即使the docs 明确指出:
在您的测试调用 createApplication() 之前,测试用例不会调用 onCreate()。这使您有机会在 onCreate() 之前设置或调整任何其他框架或测试逻辑。
这是一个彻头彻尾的谎言——它没有给我机会!
【问题讨论】:
-
据我所知,只有两种方法可以解决此问题:1) 重写 InstrumentationTestRunner 以不调用
callApplicationOnCreate并将其留给测试用例来决定是否需要或不是。不确定这会对其他测试产生什么影响。 2) 重写我们的 onCreate 方法,使其无副作用且具有幂等性(请记住,它将被调用两次,因为要使测试用例正常工作,您还必须调用createApplication)。想法? -
所以,我创建了一个绕过
callApplicationOnCreate的自定义测试运行器,现在一切都按unit 测试的预期运行。对于功能测试,我仍然需要执行完整启动的默认测试运行程序,所以我现在有两个测试运行程序,这有点痛苦。 -
有趣的文章。仅针对适当的测试用例类型绕过
callApplicationOnCreate是否足够简单,允许您为测试运行程序创建一个可以发送到上游的补丁? -
如果您使用构建系统或以其他方式通过命令行(即 ADB/Activity Manager)运行测试,那么这很简单:1)将您的单元测试保存在单独的包中您的功能测试; 2) 调用
adb shell am instrument时,将被测包与适当的测试运行器配对。当然,这意味着将有两个单独的测试运行,但我想这很公平。在 Eclipse/ADT 中事情变得更加复杂。在这里,您必须为每个测试用例定义要在 Eclipse 运行配置中使用的测试运行器。 -
我们现在是 2014 年。这个问题有没有更好的解决方案?
标签: android unit-testing