the problem with the way we build apps and why your current approach will tire you out 2.Types of Testing: Unit, Instrumentation & UI 3.Android Testing in Action: let’s get our hands dirty ;)
with how we do things now ‣ We are building application that are unnecessarily complex. ‣ It become developers are scared to refactor code and not confident about their changes. ‣ We have to test that previous features working properly. ‣ They are not sure about regression testing. ‣ Doing this manually every time is tiring. It makes you prone to mistakes and does not scale
{ /* Compare two equal Notes */ /* Compare notes with 2 different ids */ /* Compare two notes with different timestamps */ /* Compare two notes with different titles */ }
final String[] monthNumbers = {"01","02","03","04","05","06","07","08","09","10","11","12"}; public static final String[] months = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"}; public static final String GET_MONTH_ERROR = "Error. Invalid month number."; public static final String DATE_FORMAT = "MM-yyyy"; public static String getCurrentTimeStamp() throws Exception{ try { SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT); return dateFormat.format(new Date()); // Find todays date } catch (Exception e) { e.printStackTrace(); throw new Exception("Couldn't format the date into MM-yyyy"); } } public static String getMonthFromNumber(String monthNumber){ switch(monthNumber){ case "01":{ return months[0]; }.. default:{ return GET_MONTH_ERROR; } } } }
TIMESTAMP_1 = "05-2019" @JvmField val TEST_NOTE_1 = Note("Take out the trash", "It's garbage day tomorrow.", TIMESTAMP_1) const val TIMESTAMP_2 = "06-2019" val TEST_NOTE_2 = Note("Anniversary gift", "Buy an anniversary gift.", TIMESTAMP_2) @JvmField val TEST_NOTES_LIST: List<Note> = mutableListOf(Note(1, "Take out the trash", "It's garbage day tomorrow.", TIMESTAMP_1), Note(2, "Anniversary gift", "Buy an anniversary gift.", TIMESTAMP_2)) } /* Will use like blow in android tests rectories */ val note = Note(TestUtil.TEST_NOTE_1)
liveData) throws InterruptedException { final List<T> data = new ArrayList<>(); // latch for blocking thread until data is set final CountDownLatch latch = new CountDownLatch(1); Observer<T> observer = new Observer<T>() { @Override public void onChanged(T t) { data.add(t); latch.countDown(); // release the latch liveData.removeObserver(this); } }; liveData.observeForever(observer); try { latch.await(2, TimeUnit.SECONDS); // wait for onChanged to fire and set d } catch (InterruptedException e) { throw new InterruptedException("Latch failure"); } if(data.size() > 0){ return data.get(0); } return null; } }
{ /** * Test both ways to navigate from NotesListActivity to NoteActivity */ @Test fun test_navNotesListActivity() { val activityScenario = ActivityScenario.launch(NotesListActivity::class.java) onView(withId(R.id.fab)).perform(click()) onView(withId(R.id.note_text)).check(matches(isDisplayed())) pressBack() onView(withId(R.id.parent)).check(matches(isDisplayed())) } }
TITLE_IN_TEST = "Android Night" @get:Rule val activityRule = ActivityScenarioRule(NotesListActivity::class.java) @Before fun registerIdlingResource() { IdlingRegistry.getInstance().register(EspressoIdlingResource.countingIdlingResource) } @After fun unregisterIdlingResource() { IdlingRegistry.getInstance().unregister(EspressoIdlingResource.countingIdlingResource) } @Test fun a_test_isNotesListActivityVisible_onAppLaunch() { onView(withId(R.id.recyclerview)).check(matches(isDisplayed())) } @Test fun test_selectListItem_isNoteActivityVisible() { // Click list item #LIST_ITEM_IN_TEST onView(withId(R.id.recyclerview)) .perform(actionOnItemAtPosition<NotesRecyclerAdapter.ViewHolder>(LIST_ITEM_IN_TEST, click())) // Confirm nav to NoteActivity and display title onView(withId(R.id.note_text_title)).check(matches(withText(TITLE_IN_TEST))) } }
0 val TITLE_IN_TEST = "Android Night" @get:Rule val activityRule = ActivityScenarioRule(NotesListActivity::class.java) @get: Rule val espressoIdlingResoureRule = EspressoIdlingResourceRule() @Test fun a_test_isNotesListActivityVisible_onAppLaunch() { onView(withId(R.id.recyclerview)).check(matches(isDisplayed())) } @Test fun test_selectListItem_isNoteActivityVisible() { // Click list item #LIST_ITEM_IN_TEST onView(withId(R.id.recyclerview)) .perform(actionOnItemAtPosition<NotesRecyclerAdapter.ViewHolder>(LIST_ITEM_IN_TEST, click())) // Confirm nav to NoteActivity and display title onView(withId(R.id.note_text_title)).check(matches(withText(TITLE_IN_TEST))) } }
long run it will save you from many headaches. - Before you jump into android testing, ask yourself: is my architecture simplified and ‘testable’? Have I defined the scope, speed and fidelity of the tests I want to run? - Hands on Custom Test Template, Common Resources for Tests, Test Coverage, Test Suites, Firebase Test Lab, Custom Test Rules - Keep learning: https://junit.org/junit5/docs/current/user-guide/ #writing-tests, https://github.com/mannodermaus/android-junit5 Key Takeaways