Espresso Test Failed: Wanted to match 1 intents. Actually matched 0 intents

孤街醉人 提交于 2020-02-21 13:08:35

问题


I am trying to test that my SplashActivity correctly launches HomeActivity if the user is logged in.

I have looked at related questions on StackOverflow, this seems to be a common issue, but I just cannot seem to get anything to work.

I have watched the test execute on my device and visually verified that SplashActivity is indeed launching HomeActivity.

HomeActivity expects a plain intent with no data.

Here is the full error:

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

Initial Attempt:

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();
    }
}

Second Attempt: (Trying different matching syntax)

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();
    }
}

Third Attempt (Adding an Idling Resource on the destination activity)

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;
    }
}

Here is my base test. It registers an idling resource on my background processes (clean architecture use cases):

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:


Asserts that the given matcher matches one and only one intent sent by the application under test. This is an equivalent of verify(mock, times(1)) in Mockito. Verification does not have to occur in the same order as the intents were sent. Intents are recorded from the time that Intents.init is called

See documentation

When you use IntentsTestRule Intents.init() will complated after activity created. As far as I understand, you starting HomeActivity in SplashActivity.onCreate and finish SplashActivity.

So you can use ActivityTestRule and call Intents.init() before launching activity, like this:

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();
    }
}


来源:https://stackoverflow.com/questions/42400522/espresso-test-failed-wanted-to-match-1-intents-actually-matched-0-intents

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!