Espresso测试失败:想要匹配1个意图。实际上匹配0意图

时间:2017-02-22 19:26:06

标签: android-espresso dagger-2

我正在尝试测试我的SplashActivity如果用户已登录,则会正确启动HomeActivity。

我已经看过StackOverflow上的相关问题,这似乎是一个常见的问题,但我似乎无法让任何事情发挥作用。

我已经看到测试在我的设备上执行,并在视觉上验证了SplashActivity确实正在启动HomeActivity。

HomeActivity需要一个没有数据的简单意图。

以下是完整错误:

IntentMatcher: has component: has component with: class name: is "com.shoeboxed.fetch.presentation.ui.activities.HomeActivity" package name: an instance of java.lang.String short class name: an instance of java.lang.String

初步尝试:

public class SplashActivityTest extends EspressoIntegrationTest {

    @Mock
    UserRepository userRepository;

    @Rule
    public IntentsTestRule<SplashActivity> activityRule = new IntentsTestRule<>(SplashActivity.class, true, false);

    @Test
    public void loggedInUser() {
        User user = Fakes.user();
        when(userRepository.getUser()).thenReturn(user);
        doNothing().when(userRepository).refreshTeam();

        activityRule.launchActivity(new Intent());
        intended(hasComponent(HomeActivity.class.getName()));

        verify(userRepository, times(1)).getUser();
        verify(userRepository, times(1)).refreshTeam();
    }
}

第二次尝试: (尝试不同的匹配语法)

public class SplashActivityTest extends EspressoIntegrationTest {

    @Mock
    UserRepository userRepository;

    @Rule
    public IntentsTestRule<SplashActivity> activityRule = new IntentsTestRule<>(SplashActivity.class, true, false);

    @Test
    public void loggedInUser() {
        User user = Fakes.user();
        when(userRepository.getUser()).thenReturn(user);
        doNothing().when(userRepository).refreshTeam();

        activityRule.launchActivity(new Intent());
        intended(hasComponent(new ComponentName(getTargetContext(), HomeActivity.class)));

        verify(userRepository, times(1)).getUser();
        verify(userRepository, times(1)).refreshTeam();
    }
}

第三次尝试 (在目标活动上添加空闲资源)

public class SplashActivityTest extends EspressoIntegrationTest {

    @Mock
    UserRepository userRepository;

    @Rule
    public IntentsTestRule<SplashActivity> activityRule = new IntentsTestRule<>(SplashActivity.class, true, false);

    @Test
    public void loggedInUser() {
        User user = Fakes.user();
        when(userRepository.getUser()).thenReturn(user);
        doNothing().when(userRepository).refreshTeam();

        WaitActivityIsResumedIdlingResource resource = new WaitActivityIsResumedIdlingResource(HomeActivity.class.getName());
        Espresso.registerIdlingResources(resource);

        activityRule.launchActivity(new Intent());
        intended(hasComponent(new ComponentName(getTargetContext(), HomeActivity.class)));

        verify(userRepository, times(1)).getUser();
        verify(userRepository, times(1)).refreshTeam();
        Espresso.unregisterIdlingResources(resource);
    }


    private static class WaitActivityIsResumedIdlingResource implements IdlingResource {
        private final ActivityLifecycleMonitor instance;
        private final String activityToWaitClassName;
        private volatile ResourceCallback resourceCallback;
        boolean resumed = false;
        public WaitActivityIsResumedIdlingResource(String activityToWaitClassName) {
            instance = ActivityLifecycleMonitorRegistry.getInstance();
            this.activityToWaitClassName = activityToWaitClassName;
    }

    @Override
    public String getName() {
        return this.getClass().getName();
    }

    @Override
    public boolean isIdleNow() {
        resumed = isActivityLaunched();
        if(resumed && resourceCallback != null) {
            resourceCallback.onTransitionToIdle();
        }

        return resumed;
    }

    private boolean isActivityLaunched() {
        Collection<Activity> activitiesInStage = instance.getActivitiesInStage(Stage.RESUMED);
        for (Activity activity : activitiesInStage) {
            if(activity.getClass().getName().equals(activityToWaitClassName)){
                return true;
            }
        }
        return false;
    }

    @Override
    public void registerIdleTransitionCallback(IdlingResource.ResourceCallback resourceCallback) {
        this.resourceCallback = resourceCallback;
    }
}

这是我的基础测试。它在我的后台进程(干净的架构用例)上注册了一个空闲资源:

public class EspressoIntegrationTest {

    private static IdlingResource idlingThreadPool;

    private AppComponent oldComponent = app().appComponent();

    @Rule
    public DaggerMockRule<AppComponent> daggerRule = new DaggerMockRule<>(AppComponent.class, new AppModule(app()))
            .set(component -> {
                oldComponent = app().appComponent();
                app().setAppComponent(component);
            });

    @BeforeClass
    public static void registerResources() {
        idlingThreadPool = getIdlingThreadExecutor();
        Espresso.registerIdlingResources(idlingThreadPool);
    }

    @AfterClass
    public static void deregister() {
        Espresso.unregisterIdlingResources(idlingThreadPool);
    }

    @After
    public void resetApp() {
        app().setAppComponent(oldComponent);
    }

    private static IdlingResource getIdlingThreadExecutor() {
        return (IdlingResource) jobExecutor().getThreadPoolExecutor();
    }

    private static JobExecutor jobExecutor() {
        return ((JobExecutor) app().appComponent().threadExecutor());
    }

    private static App app() {
        return (App) getInstrumentation().getTargetContext().getApplicationContext();
    }
}

1 个答案:

答案 0 :(得分:10)

  

断言给定匹配器匹配被测应用程序发送的一个且仅一个意图。这相当于Mockito中的验证(mock,times(1))。验证不必以与发送意图相同的顺序发生。 从调用Intents.init的时间开始记录意图

See documentation

使用IntentsTestRuleIntents.init()会在创建活动后进行编译。据我了解,您在HomeActivity中开始SplashActivity.onCreate并完成SplashActivity

因此,您可以在启动活动之前使用ActivityTestRule并致电Intents.init(),如下所示:

public class SplashActivityTest extends EspressoIntegrationTest {

    @Mock
    UserRepository userRepository;

    @Rule
    public ActivityTestRule<SplashActivity> activityRule = new ActivityTestRule<>(SplashActivity.class, true, false);


    @Before
    public void setUp() throws Exception{
        Intents.init();
    }

    @Test
    public void loggedInUser() {
        User user = Fakes.user();
        when(userRepository.getUser()).thenReturn(user);
        doNothing().when(userRepository).refreshTeam();

        activityRule.launchActivity(new Intent());
        intended(hasComponent(HomeActivity.class.getName()));

        verify(userRepository, times(1)).getUser();
        verify(userRepository, times(1)).refreshTeam();
    }


    @After
    public void tearDown() throws Exception{
        Intents.release();
    }
}