我有一个Android应用,其MyApplication
类继承自Application
。
我创建了几个与@RunWith(AndroidJUnit4.class)
一起运行的单元测试。如果我单独运行每个测试,他们都会通过。如果我一起运行 - 第一个通过然后(其中一些)其他人失败。
问题是,似乎只创建了MyApplication
的一个实例,然后将其保留并用于导致失败的所有测试,因为MyApplication
中有一个状态必须只被初始化一次。
有没有办法运行单元测试(androidTest),以便为每个测试重新启动应用程序?我不在乎它是否会很慢(例如每次都必须重新安装应用程序)我只是想让测试彼此独立运行。
单元测试的实际代码如下(根据@Zinc的要求):
@RunWith(AndroidJUnit4.class)
public class AutoLogin_ActMainTest {
@Rule
public ActivityTestRule<ActMain> mActivityRule = new ActivityTestRule<ActMain>(
ActMain.class) {
@Override
protected void beforeActivityLaunched() {
super.beforeActivityLaunched();
MyTestApp app = (MyTestApp) InstrumentationRegistry.getInstrumentation().getTargetContext().getApplicationContext();
DependencyInjector.reset();
app.reset();
FakeUnitDaggerModule fudm = new FakeUnitDaggerModule();
Session session = new SessionImpl(new TimeProviderImpl());
fudm.setResMain(new ResMainTest(session));
FakeAppPrefs appPrefs = new FakeAppPrefs();
FakeLoginPrefs loginPrefs = new FakeLoginPrefs();
CurrentUserHolder currentUserHolder = new CurrentUserHolder();
FakeComponent inj = DaggerFakeComponent.builder().
fakeMyAppDaggerModule(new FakeMyAppDaggerModule(app, appPrefs, loginPrefs, currentUserHolder)).
appInfoDaggerModule(new AppInfoDaggerModule("1")).
fakeSessionDaggerModule(new FakeSessionDaggerModule(session)).
fakeExchangeDaggerModule(new FakeExchangeDaggerModule("https://test.com")).
fakeUnitDaggerModule(fudm).
build();
DependencyInjector.init(inj);
DependencyInjector.getInstance().inject(app);
app.onStart();
}
};
@Test
public void testAutoLogin() {
ElapsedTimeIdlingResource idlingResource = new ElapsedTimeIdlingResource(500);
Espresso.registerIdlingResources(idlingResource);
idlingResource.startWaiting();
onView(ViewMatchers.withId(R.id.tv_logged_in_as)).check(matches(isDisplayed()));
Espresso.unregisterIdlingResources(idlingResource);
}
}
答案 0 :(得分:4)
问题在于,似乎只创建了一个MyApplication实例,然后将其保留并用于导致失败的所有测试,因为MyApplication中的状态必须只初始化一次。
恕我直言,这是应用程序中的一个错误,应该修复。 Application
对于许多真正的业务逻辑来说是一个不合适的位置(尽管它可以初始化崩溃报告库,StrictMode
等)。其他所有东西都应该单独测试,可以直接测试,通过模拟,依赖注入等等。
话虽如此,有时问题不是你控制的代码,而是来自库或框架的代码。
有没有办法运行单元测试(androidTest),以便每次测试重启应用程序?
现在,是的,虽然当时没有问过这个问题。 Android Test Orchestrator(ATO)可以更好地隔离每个测试,但代价是测试执行速度。
答案 1 :(得分:2)
您需要重构您的应用程序,以便控制状态的任何代码都不会绑定到应用程序类,而是绑定到另一个对象中。然后,您可以重置该部分或模拟它,而无需关心应用程序类的持久性。优选地,这是使用一些形式依赖性注入来完成的。
答案 2 :(得分:0)
我对你的问题不太确定。但您可以使用ApplicationTestCase。有点像:
public class MyApplicationTest extends ApplicationTestCase<MyTestApp> {
public void test1() {
createApplication();
... test here ...
terminateApplication();
}
public void test2() {
createApplication();
... test here ...
terminateApplication();
}
}
参考:https://developer.android.com/reference/android/test/ApplicationTestCase.html
答案 3 :(得分:0)
public class TestApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
// Sdk.terminate(); - If you specify TestApplication as an
// application class in AndroidManifest,
// you'll have to uncomment this(due to issue with test runner)
Sdk.initialize();
}
@Override
public void onTerminate() {
super.onTerminate();
Sdk.terminate();
}
}
Sdk课程
public class Sdk {
private static Sdk sInstance;
private void Sdk(){
}
public static Sdk getInstance() throws RuntimeException {
if (sInstance == null) {
throw new RuntimeException();
}
return sInstance;
}
public static void terminate() {
sInstance = null;
}
public static void initialize() {
if (sInstance == null) {
sInstance = new Sdk();
//save some information according to what is on the default configurations
} else {
throw new RuntimeException("Method was already initialized");
}
}}
试验:
public class MyApplicationTest extends ApplicationTestCase<TestApplication> {
public MyApplicationTest() {
super(TestApplication.class);
}
public void testMultiplicationTests() {
createApplication();
int answer = 42;
assertEquals(42, answer);
terminateApplication();
}
public void testDefaultSettings() {
createApplication();
assertNotNull(Sdk.getInstance());
terminateApplication();
}}