Upload
k-matthew-dupree
View
86
Download
1
Embed Size (px)
Citation preview
Tested apps are better apps, but building them is tough. They have seams. DI gives you Object Seams, which is why MVP helps testability.
Build Variants give you Link Seams, but don’t overuse
them.
Tested apps are better apps, but building them is tough. They have seams. DI gives you Object Seams, which is why MVP helps testability.
Build Variants give you Link Seams, but don’t overuse
them.
–Steve Freeman and Nat Pryce, authors of Growing Object Oriented Software Guided by Tests
“for a class to be easy to unit-test, the class must…be loosely coupled and highly cohesive
—in other words, well-designed.”
“We invest in this huge testing framework…
engineers here have the power to try out an idea
and ship it to maybe 10,000 people or 100,000
people.”
Tested apps are better apps, but building them is tough. They have seams. DI gives you Object Seams, which is why MVP helps testability.
Build Variants give you Link Seams, but don’t overuse
them.
Tested apps are better apps, but building them is tough. They have seams. DI gives you Object Seams, which is why MVP helps testability.
Build Variants give you Link Seams, but don’t overuse
them.
–Michael Feathers, Working Effectively with Legacy Code
“One of the things that nearly everyone notices when they try to write tests for existing code is
just how poorly suited code is to testing.”
public class PresenterFragmentImpl extends Fragment implements Presenter, UpdatableView.UserActionListener, LoaderManager.LoaderCallbacks<Cursor> {
@Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { Loader<Cursor> cursorLoader = createLoader(id, args); mLoaderIdlingResource.onLoaderStarted(cursorLoader); return cursorLoader; }
@Override public void onLoadFinished(Loader<Cursor> loader, Cursor data) { processData(loader, data); mLoaderIdlingResource.onLoaderFinished(loader); } }
public class PresenterFragmentImpl extends Fragment implements Presenter, UpdatableView.UserActionListener, LoaderManager.LoaderCallbacks<Cursor> {
@Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { Loader<Cursor> cursorLoader = createLoader(id, args); mLoaderIdlingResource.onLoaderStarted(cursorLoader); return cursorLoader; }
@Override public void onLoadFinished(Loader<Cursor> loader, Cursor data) { processData(loader, data); mLoaderIdlingResource.onLoaderFinished(loader); } }
@Override public void onSharedPreferenceChanged(SharedPreferences sharedPrefs,
String key) {
if (SettingsUtils.PREF_SYNC_CALENDAR.equals(key)) { Intent intent; if (SettingsUtils.shouldSyncCalendar(getActivity())) { // Add all calendar entries intent = new Intent(ACTION_UPDATE_ALL_SESSIONS_CALENDAR); } else { // Remove all calendar entries intent = new Intent(ACTION_CLEAR_ALL_SESSIONS_CALENDAR); }
intent.setClass(getActivity(), SessionCalendarService.class); getActivity().startService(intent); } }
@Override public void onSharedPreferenceChanged(SharedPreferences sharedPrefs,
String key) {
if (SettingsUtils.PREF_SYNC_CALENDAR.equals(key)) { Intent intent; if (SettingsUtils.shouldSyncCalendar(getActivity())) { // Add all calendar entries intent = new Intent(ACTION_UPDATE_ALL_SESSIONS_CALENDAR); } else { // Remove all calendar entries intent = new Intent(ACTION_CLEAR_ALL_SESSIONS_CALENDAR); }
intent.setClass(getActivity(), SessionCalendarService.class); getActivity().startService(intent); } }
@Override public void onSharedPreferenceChanged(SharedPreferences sharedPrefs,
String key) {
if (SettingsUtils.PREF_SYNC_CALENDAR.equals(key)) { Intent intent; if (SettingsUtils.shouldSyncCalendar(getActivity())) { // Add all calendar entries intent = new Intent(ACTION_UPDATE_ALL_SESSIONS_CALENDAR); } else { // Remove all calendar entries intent = new Intent(ACTION_CLEAR_ALL_SESSIONS_CALENDAR); }
intent.setClass(getActivity(), SessionCalendarService.class); getActivity().startService(intent); } }
@Test public void onSPChangedRemovesSessions() throws Exception { // Arrange
//Act mSettingsFragment.onSPChanged(mMockSharedPreferences, PREF_SYNC_CALENDAR);
//Assert
}
@Test public void onSPChangedRemovesSessions() throws Exception { // Arrange
//Act mSettingsFragment.onSPChanged(mMockSharedPreferences, PREF_SYNC_CALENDAR);
//Assert
}
@Override public void onSharedPreferenceChanged(SharedPreferences sharedPrefs,
String key) {
if (SettingsUtils.PREF_SYNC_CALENDAR.equals(key)) { Intent intent; if (SettingsUtils.shouldSyncCalendar(getActivity())) { // Add all calendar entries intent = new Intent(ACTION_UPDATE_ALL_SESSIONS_CALENDAR); } else { // Remove all calendar entries intent = new Intent(ACTION_CLEAR_ALL_SESSIONS_CALENDAR); }
intent.setClass(getActivity(), SessionCalendarService.class); getActivity().startService(intent); } }
@Test public void onSPChangedRemovesSessions() throws Exception { // Arrange
//Act mSettingsFragment.onSPChanged(mMockSharedPreferences, PREF_SYNC_CALENDAR);
//Assert
}
@Override public void onSharedPreferenceChanged(SharedPreferences sharedPrefs,
String key) {
if (SettingsUtils.PREF_SYNC_CALENDAR.equals(key)) { Intent intent; if (SettingsUtils.shouldSyncCalendar(getActivity())) { // Add all calendar entries intent = new Intent(ACTION_UPDATE_ALL_SESSIONS_CALENDAR); } else { // Remove all calendar entries intent = new Intent(ACTION_CLEAR_ALL_SESSIONS_CALENDAR); }
intent.setClass(getActivity(), SessionCalendarService.class); getActivity().startService(intent); } }
Tested apps are better apps, but building them is tough. They have seams. DI gives you Object Seams, which is why MVP helps testability.
Build Variants give you Link Seams, but don’t overuse
them.
Tested apps are better apps, but building them is tough. They have seams. DI gives you Object Seams, which is why MVP helps testability.
Build Variants give you Link Seams, but don’t overuse
them.
–Michael Feathers, author of Working Effectively with Legacy Code
“A seam is a place where you can alter behavior in your program without editing in that
place.”
class CalendarUpdatingOnSharedPreferenceChangedListener {
void onPreferenceChanged(CalendarPreferences calendarPreferences, String key) {
if (SettingsUtils.PREF_SYNC_CALENDAR.equals(key)) { if (calendarPreferences.shouldSyncCalendar()) { mSessUpdaterLauncher.launchAddAllSessionsUpdater(); } else { mSessUpdaterLauncher.launchClearAllSessionsUpdate(); } } } }
class CalendarUpdatingOnSharedPreferenceChangedListener {
void onPreferenceChanged(CalendarPreferences calendarPreferences, String key) {
if (SettingsUtils.PREF_SYNC_CALENDAR.equals(key)) { if (calendarPreferences.shouldSyncCalendar()) { mSessUpdaterLauncher.launchAddAllSessionsUpdater(); } else { mSessUpdaterLauncher.launchClearAllSessionsUpdate(); } } } }
class CalendarUpdatingOnSharedPreferenceChangedListener {
void onPreferenceChanged(CalendarPreferences calendarPreferences, String key) {
if (SettingsUtils.PREF_SYNC_CALENDAR.equals(key)) { if (calendarPreferences.shouldSyncCalendar()) { mSessUpdaterLauncher.launchAddAllSessionsUpdater(); } else { mSessUpdaterLauncher.launchClearAllSessionsUpdate(); } } } }
class CalendarUpdatingOnSharedPreferenceChangedListener {
void onPreferenceChanged(CalendarPreferences calendarPreferences, String key) {
if (SettingsUtils.PREF_SYNC_CALENDAR.equals(key)) { if (calendarPreferences.shouldSyncCalendar()) { mSessUpdaterLauncher.launchAddAllSessionsUpdater(); } else { mSessUpdaterLauncher.launchClearAllSessionsUpdate(); } } } }
@Test public void onPreferenceChangedClearedCalendar() throws Exception {
// Arrange CUOSPCListener listener = new CUOSPCListener(mSessionUpdateLauncher);
final CalendarPreferences calendarPreferences = mock(CalendarPreferences.class); when(calendarPreferences.shouldSyncCalendar()).thenReturn(false);
// Act listener.onPreferenceChanged(calendarPreferences, SettingsUtils.PREF_SYNC_CALENDAR);
// Assert verify(mSessionUpdateLauncher).launchClearAllSessionsUpdate(); }
@Test public void onPreferenceChangedClearedCalendar() throws Exception {
// Arrange CUOSPCListener listener = new CUOSPCListener(mSessionUpdateLauncher);
final CalendarPreferences calendarPreferences = mock(CalendarPreferences.class); when(calendarPreferences.shouldSyncCalendar()).thenReturn(false);
// Act listener.onPreferenceChanged(calendarPreferences, SettingsUtils.PREF_SYNC_CALENDAR);
// Assert verify(mSessionUpdateLauncher).launchClearAllSessionsUpdate(); }
@Test public void onPreferenceChangedClearedCalendar() throws Exception {
// Arrange CUOSPCListener listener = new CUOSPCListener(mSessionUpdateLauncher);
final CalendarPreferences calendarPreferences = mock(CalendarPreferences.class); when(calendarPreferences.shouldSyncCalendar()).thenReturn(false);
// Act listener.onPreferenceChanged(calendarPreferences, SettingsUtils.PREF_SYNC_CALENDAR);
// Assert verify(mSessionUpdateLauncher).launchClearAllSessionsUpdate(); }
class CalendarUpdatingOnSharedPreferenceChangedListener {
void onPreferenceChanged(CalendarPreferences calendarPreferences, String key) {
if (SettingsUtils.PREF_SYNC_CALENDAR.equals(key)) { if (calendarPreferences.shouldSyncCalendar()) { mSessUpdaterLauncher.launchAddAllSessionsUpdater(); } else { mSessUpdaterLauncher.launchClearAllSessionsUpdate(); } } } }
Tested apps are better apps, but building them is tough. They have seams. DI gives you Object Seams, which is why MVP helps testability.
Build Variants give you Link Seams, but don’t overuse
them.
Tested apps are better apps, but building them is tough. They have seams. DI gives
you Object Seams, which is why MVP helps testability.
Build Variants give you Link Seams, but don’t overuse
them.
–Michael Feathers
“The fundamental thing to recognize is that when we look at a call in an object-oriented
program, it does not define which method will actually be executed.”
Tested apps are better apps, but building them is tough. They have seams. DI gives
you Object Seams, which is why MVP helps testability.
Build Variants give you Link Seams, but don’t overuse
them.
Tested apps are better apps, but building them is tough. They have seams. DI gives
you Object Seams, which is why MVP helps testability. Build Variants give you Link Seams, but don’t overuse
them.
private void setupCards(CollectionView.Inventory inventory) { if (SettingsUtils.isAttendeeAtVenue(getContext())) { if (!hasAnsweredConfMessageCardsPrompt(getContext())) { inventoryGroup = new InventoryGroup(GROUP_ID_MESSAGE_CARDS);
MessageData conferenceMessageOptIn = MessageCardHelper .getConferenceOptInMessageData(getContext()); inventoryGroup.addItemWithTag(conferenceMessageOptIn); inventoryGroup.setDisplayCols(1); inventory.addGroup(inventoryGroup); } // ... } }
private void setupCards(CollectionView.Inventory inventory) { if (SettingsUtils.isAttendeeAtVenue(getContext())) { if (!hasAnsweredConfMessageCardsPrompt(getContext())) { inventoryGroup = new InventoryGroup(GROUP_ID_MESSAGE_CARDS);
MessageData conferenceMessageOptIn = MessageCardHelper .getConferenceOptInMessageData(getContext()); inventoryGroup.addItemWithTag(conferenceMessageOptIn); inventoryGroup.setDisplayCols(1); inventory.addGroup(inventoryGroup); } // ... } }
private void setupCards(CollectionView.Inventory inventory) { if (SettingsUtils.isAttendeeAtVenue(getContext())) { if (!hasAnsweredConfMessageCardsPrompt(getContext())) { inventoryGroup = new InventoryGroup(GROUP_ID_MESSAGE_CARDS);
MessageData conferenceMessageOptIn = MessageCardHelper .getConferenceOptInMessageData(getContext()); inventoryGroup.addItemWithTag(conferenceMessageOptIn); inventoryGroup.setDisplayCols(1); inventory.addGroup(inventoryGroup); } // ... } }
class Presenter {
public void presentCards() {
if (mIsAttendeeAtVenue) {
if (!mMsgSettings.hasAnsweredMessagePrompt()) {
mExploreView.addMessageOptInCard();
} // Stuff } } }
class Presenter {
public void presentCards() {
if (mIsAttendeeAtVenue) {
if (!mMsgSettings.hasAnsweredMessagePrompt()) {
mExploreView.addMessageOptInCard();
} // Stuff } } }
class Presenter {
public void presentCards() {
if (mIsAttendeeAtVenue) {
if (!mMsgSettings.hasAnsweredMessagePrompt()) {
mExploreView.addMessageOptInCard();
} // Stuff } } }
class Presenter {
public void presentCards() {
if (mIsAttendeeAtVenue) {
if (!mMsgSettings.hasAnsweredMessagePrompt()) {
mExploreView.addMessageOptInCard();
} // Stuff } } }
Tested apps are better apps, but building them is tough. They have seams. DI gives
you Object Seams, which is why MVP helps testability. Build Variants give you Link Seams, but don’t overuse
them.
Tested apps are better apps, but building them is tough. They have seams. DI gives you Object Seams, which is why MVP helps testability.
Build Variants give you Link Seams, but don’t overuse
them.
–Michael Feathers
“[code] contains calls to code in other files. Linkers…resolve each of the calls so that you
can have a complete program at runtime…you can usually exploit [this] to substitute pieces of
your program”
public class PresenterFragmentImpl extends Fragment implements Presenter, UpdatableView.UserActionListener, LoaderManager.LoaderCallbacks<Cursor> {
@Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { Loader<Cursor> cursorLoader = createLoader(id, args); mLoaderIdlingResource.onLoaderStarted(cursorLoader); return cursorLoader; }
@Override public void onLoadFinished(Loader<Cursor> loader, Cursor data) { processData(loader, data); mLoaderIdlingResource.onLoaderFinished(loader); } }
public class PresenterFragmentImpl extends Fragment implements Presenter, UpdatableView.UserActionListener, LoaderManager.LoaderCallbacks<Cursor> {
@Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { Loader<Cursor> cursorLoader = createLoader(id, args); mLoaderIdlingResource.onLoaderStarted(cursorLoader); return cursorLoader; }
@Override public void onLoadFinished(Loader<Cursor> loader, Cursor data) { processData(loader, data); mLoaderIdlingResource.onLoaderFinished(loader); } }
public PresenterFragmentImpl addPresenterFragment(int uVResId, Model model, QueryEnum[] queries,
UserActionEnum[] actions){ //...
if (presenter == null) { //Create, set up and add the presenter. presenter = new PresenterFragmentImpl(); //... } else {
//... } return presenter; }
public PresenterFragmentImpl addPresenterFragment(int uVResId, Model model, QueryEnum[] queries,
UserActionEnum[] actions){ //...
if (presenter == null) { //Create, set up and add the presenter. presenter = new PresenterFragmentImpl(); //... } else {
//... } return presenter; }
flavorDimensions 'datasource', 'features' productFlavors { mock { dimension 'datasource' }
prod { dimension 'datasource' }
free { dimension 'features' } }
Tested apps are better apps, but building them is tough. They have seams. DI gives you Object Seams, which is why MVP helps testability.
Build Variants give you Link Seams, but don’t overuse
them.
Tested apps are better apps, but building them is tough. They have seams. DI gives you Object Seams, which is why MVP helps testability.
Build Variants give you Link Seams, but don’t overuse
them.
public class FragFactory { public PresenterFragmentImpl make() { return new PresenterFragmentImpl(); } }
public class FragFactory { public PresenterFragmentImpl make() { return new MockPresenterFragmentImpl(); } }
public class FragFactory { public PresenterFragmentImpl make() { return new PresenterFragmentImpl(); } }
public class FragFactory { public PresenterFragmentImpl make() { return new MockPresenterFragmentImpl(); } }
public class FragFactory { public PresenterFragmentImpl make() { return new PresenterFragmentImpl(); } }
public class FragFactory { public PresenterFragmentImpl make() { return new MockPresenterFragmentImpl(); } }
public PresenterFragmentImpl addPresenterFragment(int uVResId, Model model, QueryEnum[] queries,
UserActionEnum[] actions){ //...
if (presenter == null) { //Create, set up and add the presenter. presenter = new PresenterFragmentImpl(); // 1 seam //... } else {
//... } return presenter; }
public PresenterFragmentImpl addPresenterFragment(int uVResId, Model model, QueryEnum[] queries,
UserActionEnum[] actions){ //...
if (presenter == null) { //Create, set up and add the presenter. presenter = mFragFactory.make(); // 2 seams //... } else {
//... } return presenter; }
Tested apps are better apps, but building them is tough. They have seams. DI gives you Object Seams, which is why MVP helps testability.
Build Variants give you Link Seams, but don’t overuse
them.
Tested apps are better apps, but building them is tough. They have seams. DI gives you Object Seams, which is why MVP helps testability.
Build Variants give you Link Seams, but don’t overuse
them.