SlideShare a Scribd company logo
If Android Tests
Could Talk
Matt Dupree
Their pace has quickened
This code is garbage
–Steve Freeman and Nat Pryce, 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.”
–Steve Freeman and Nat Pryce, 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.”
–Steve Freeman and Nat Pryce, 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.”
Activities tend to have tight
coupling and poor cohesion
Application components (activities,
services, providers, receivers) are interfaces
for your application to interact with the
operating system; don’t take them as a
recommendation of the facilities you
should architect your entire application
around.
—Chet Haase, Developing for Android VII
Activities tend to have tight
coupling and poor cohesion
@Override

public void onRequestPermissionsResult(final int requestCode,

@NonNull final String[] permissions,

@NonNull final int[] grantResults) {



if (requestCode != REQUEST_LOCATION_PERMISSION) {

return;

}



if (permissions.length == 1 &&

LOCATION_PERMISSION.equals(permissions[0]) &&

grantResults[0] == PackageManager.PERMISSION_GRANTED) {

// Permission has been granted.

if (mMapFragment != null) {

mMapFragment.setMyLocationEnabled(true);

}

} else {

// Permission was denied. Display error message.

Toast.makeText(this, R.string.map_permission_denied,

Toast.LENGTH_SHORT).show();

}

super.onRequestPermissionsResult(requestCode, permissions,

grantResults);

}
@Override

public void onRequestPermissionsResult(final int requestCode,

@NonNull final String[] permissions,

@NonNull final int[] grantResults) {



if (requestCode != REQUEST_LOCATION_PERMISSION) {

return;

}



if (permissions.length == 1 &&

LOCATION_PERMISSION.equals(permissions[0]) &&

grantResults[0] == PackageManager.PERMISSION_GRANTED) {

// Permission has been granted.

if (mMapFragment != null) {

mMapFragment.setMyLocationEnabled(true);

}

} else {

// Permission was denied. Display error message.

Toast.makeText(this, R.string.map_permission_denied,

Toast.LENGTH_SHORT).show();

}

super.onRequestPermissionsResult(requestCode, permissions,

grantResults);

}
@Override

public void onRequestPermissionsResult(final int requestCode,

@NonNull final String[] permissions,

@NonNull final int[] grantResults) {



if (requestCode != REQUEST_LOCATION_PERMISSION) {

return;

}



if (permissions.length == 1 &&

LOCATION_PERMISSION.equals(permissions[0]) &&

grantResults[0] == PackageManager.PERMISSION_GRANTED) {

// Permission has been granted.

if (mMapFragment != null) {

mMapFragment.setMyLocationEnabled(true);

}

} else {

// Permission was denied. Display error message.

Toast.makeText(this, R.string.map_permission_denied,

Toast.LENGTH_SHORT).show();

}

super.onRequestPermissionsResult(requestCode, permissions,

grantResults);

}
@Override

public void onRequestPermissionsResult(final int requestCode,

@NonNull final String[] permissions,

@NonNull final int[] grantResults) {



if (requestCode != REQUEST_LOCATION_PERMISSION) {

return;

}



if (permissions.length == 1 &&

LOCATION_PERMISSION.equals(permissions[0]) &&

grantResults[0] == PackageManager.PERMISSION_GRANTED) {

// Permission has been granted.

if (mMapFragment != null) {

mMapFragment.setMyLocationEnabled(true);

}

} else {

// Permission was denied. Display error message.

Toast.makeText(this, R.string.map_permission_denied,

Toast.LENGTH_SHORT).show();

}

super.onRequestPermissionsResult(requestCode, permissions,

grantResults);

}
@Override

public void onRequestPermissionsResult(final int requestCode,

@NonNull final String[] permissions,

@NonNull final int[] grantResults) {



if (requestCode != REQUEST_LOCATION_PERMISSION) {

return;

}



if (permissions.length == 1 &&

LOCATION_PERMISSION.equals(permissions[0]) &&

grantResults[0] == PackageManager.PERMISSION_GRANTED) {

// Permission has been granted.

if (mMapFragment != null) {

mMapFragment.setMyLocationEnabled(true);

}

} else {

// Permission was denied. Display error message.

Toast.makeText(this, R.string.map_permission_denied,

Toast.LENGTH_SHORT).show();

}

super.onRequestPermissionsResult(requestCode, permissions,

grantResults);

}
@Test

public void showsToastIfPermissionIsRejected()

throws Exception {

MapActivity mapActivity = new MapActivity();



mapActivity.onRequestPermissionsResult(

MapActivity.REQUEST_LOCATION_PERMISSION,

new String[]{MapActivity.LOCATION_PERMISSION}, new int[]{

PackageManager.PERMISSION_DENIED});



assertToastDisplayed();

}
@Test

public void showsToastIfPermissionIsRejected()

throws Exception {

MapActivity mapActivity = new MapActivity();



mapActivity.onRequestPermissionsResult(

MapActivity.REQUEST_LOCATION_PERMISSION,

new String[]{MapActivity.LOCATION_PERMISSION}, new int[]{

PackageManager.PERMISSION_DENIED});



assertToastDisplayed();

}
@Test

public void showsToastIfPermissionIsRejected()

throws Exception {

MapActivity mapActivity = new MapActivity();



mapActivity.onRequestPermissionsResult(

MapActivity.REQUEST_LOCATION_PERMISSION,

new String[]{MapActivity.LOCATION_PERMISSION}, new int[]{

PackageManager.PERMISSION_DENIED});



assertToastDisplayed();

}
@Test

public void showsToastIfPermissionIsRejected()

throws Exception {

MapActivity mapActivity = new MapActivity();



mapActivity.onRequestPermissionsResult(

MapActivity.REQUEST_LOCATION_PERMISSION,

new String[]{MapActivity.LOCATION_PERMISSION}, new int[]{

PackageManager.PERMISSION_DENIED});



assertToastDisplayed();

}
private void assertToastDisplayed() {

// It can't be done, sir 

}
@Override

public void onRequestPermissionsResult(final int requestCode,

@NonNull final String[] permissions,

@NonNull final int[] grantResults) {



if (requestCode != REQUEST_LOCATION_PERMISSION) {

return;

}



if (permissions.length == 1 &&

LOCATION_PERMISSION.equals(permissions[0]) &&

grantResults[0] == PackageManager.PERMISSION_GRANTED) {

// Permission has been granted.

if (mMapFragment != null) {

mMapFragment.setMyLocationEnabled(true);

}

} else {

// Permission was denied. Display error message.

Toast.makeText(this, R.string.map_permission_denied,

Toast.LENGTH_SHORT).show();

}

super.onRequestPermissionsResult(requestCode, permissions,

grantResults);

}
MapActivity is tightly coupled
with MapFragment and Toast
static class OnPermissionResultListener {


private final PermittedView mPermittedView;



void onPermissionResult(final int requestCode,

final String[] permissions, final int[] grantResults) {}



interface PermittedView {


void displayPermissionDenied();
void displayPermittedView();

}

}
static class OnPermissionResultListener {


private final PermittedView mPermittedView;



void onPermissionResult(final int requestCode,

final String[] permissions, final int[] grantResults) {}



interface PermittedView {


void displayPermissionDenied();
void displayPermittedView();

}

}
static class OnPermissionResultListener {


private final PermittedView mPermittedView;



void onPermissionResult(final int requestCode,

final String[] permissions, final int[] grantResults) {}



interface PermittedView {


void displayPermissionDenied();
void displayPermittedView();

}

}
void onPermissionResult(final int requestCode,

final String[] permissions, final int[] grantResults) {

if (requestCode != MapActivity.REQUEST_LOCATION_PERMISSION) {

return;

}



if (permissions.length == 1 &&

MapActivity.LOCATION_PERMISSION.equals(permissions[0]) &&

grantResults[0] == PackageManager.PERMISSION_GRANTED) {

// Permission has been granted.

mPermittedView.displayPermittedView();



} else {

// Permission was denied. Display error message.

mPermittedView.displayPermissionDenied();

}

}
void onPermissionResult(final int requestCode,

final String[] permissions, final int[] grantResults) {

if (requestCode != MapActivity.REQUEST_LOCATION_PERMISSION) {

return;

}



if (permissions.length == 1 &&

MapActivity.LOCATION_PERMISSION.equals(permissions[0]) &&

grantResults[0] == PackageManager.PERMISSION_GRANTED) {

// Permission has been granted.

mPermittedView.displayPermittedView();



} else {

// Permission was denied. Display error message.

mPermittedView.displayPermissionDenied();

}

}
@Test

public void displaysErrorWhenPermissionRejected() throws Exception {



OnPermissionResultListener onPermissionResultListener =

new OnPermissionResultListener(mPermittedView);



onPermissionResultListener.onPermissionResult(

MapActivity.REQUEST_LOCATION_PERMISSION,

new String[]{MapActivity.LOCATION_PERMISSION},

new int[]{PackageManager.PERMISSION_DENIED});



verify(mPermittedView).displayPermissionDenied();

}
@Test

public void displaysErrorWhenPermissionRejected() throws Exception {



OnPermissionResultListener onPermissionResultListener =

new OnPermissionResultListener(mPermittedView);



onPermissionResultListener.onPermissionResult(

MapActivity.REQUEST_LOCATION_PERMISSION,

new String[]{MapActivity.LOCATION_PERMISSION},

new int[]{PackageManager.PERMISSION_DENIED});



verify(mPermittedView).displayPermissionDenied();

}
@Test

public void displaysErrorWhenPermissionRejected() throws Exception {



OnPermissionResultListener onPermissionResultListener =

new OnPermissionResultListener(mPermittedView);



onPermissionResultListener.onPermissionResult(

MapActivity.REQUEST_LOCATION_PERMISSION,

new String[]{MapActivity.LOCATION_PERMISSION},

new int[]{PackageManager.PERMISSION_DENIED});



verify(mPermittedView).displayPermissionDenied();

}
@Test

public void displaysErrorWhenPermissionRejected() throws Exception {



OnPermissionResultListener onPermissionResultListener =

new OnPermissionResultListener(mPermittedView);



onPermissionResultListener.onPermissionResult(

MapActivity.REQUEST_LOCATION_PERMISSION,

new String[]{MapActivity.LOCATION_PERMISSION},

new int[]{PackageManager.PERMISSION_DENIED});



verify(mPermittedView).displayPermissionDenied();

}
Who cares?
–Edward Yourdon and Larry Constantine
“what we are striving for is loosely coupled
systems…in which one can study (or debug, or
maintain) any one module without having to
know very much about any other modules in the
system”
@Override

public void displayPermissionDenied() {

Toast.makeText(MapActivity.this, R.string.map_permission_denied,

Toast.LENGTH_SHORT).show();

}
@Override

public void displayPermissionDenied() {

PermissionsUtils.displayConditionalPermissionDenialSnackbar(this,

R.string.map_permission_denied, PERMISSIONS,

REQUEST_LOCATION_PERMISSION, false);

}
–Kent Beck, TDD By Example
“Dependency is the key problem in software
development at all scales…if dependency is the
problem, duplication is the symptom.”
@Override

public void onRequestPermissionsResult(final int requestCode,

@NonNull final String[] permissions,

@NonNull final int[] grantResults) {



if (grantResults.length == APP_REQUIRED_PERMISSIONS.length &&

grantResults[0] == PackageManager.PERMISSION_GRANTED &&

grantResults[1] == PackageManager.PERMISSION_GRANTED) {

// If permission granted then refresh account list so user can

// select an account.

clearSnackbar();

reloadAccounts();

refreshAccountListUI();

} else {

LOGI(TAG, "onRequestPermissionResult with permissions
denied");

mSnackbar = new WeakReference<>(PermissionsUtils

.displayConditionalPermissionDenialSnackbar(

getActivity(),

R.string.welcome_permissions_rationale,

APP_REQUIRED_PERMISSIONS,

REQUEST_PERMISSION_REQUEST_CODE));

}

}
Activities tend to have tight
coupling and poor cohesion
–Steve Freeman and Nat Pryce, 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.”
Activities tend to have tight
coupling and poor cohesion
–Steve Freeman and Nat Pryce, 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.”
private void displaySessionData(final SessionDetailModel data) {
//46 statement method inside a 900 line class




// Handle Keynote as a special case, where the user cannot remove it

// from the schedule (it is auto added to schedule on sync)

mShowFab = (AccountUtils.hasActiveAccount(getContext()) &&

!data.isKeynote());

mAddScheduleFab

.setVisibility(mShowFab ? View.VISIBLE : View.INVISIBLE);



if (!data.isKeynote()) {

showInScheduleDeferred(data.isInSchedule());

}

//...


updateTimeBasedUi(data);





}
private void displaySessionData(final SessionDetailModel data) {
//46 statement method inside a 900 line class!




// Handle Keynote as a special case, where the user cannot remove it

// from the schedule (it is auto added to schedule on sync)

mShowFab = (AccountUtils.hasActiveAccount(getContext()) &&

!data.isKeynote());

mAddScheduleFab

.setVisibility(mShowFab ? View.VISIBLE : View.INVISIBLE);



if (!data.isKeynote()) {

showInScheduleDeferred(data.isInSchedule());

}

//...


updateTimeBasedUi(data);





}
private void displaySessionData(final SessionDetailModel data) {
//46 statement method inside a 900 line class




// Handle Keynote as a special case, where the user cannot remove it

// from the schedule (it is auto added to schedule on sync)

mShowFab = (AccountUtils.hasActiveAccount(getContext()) &&

!data.isKeynote());

mAddScheduleFab

.setVisibility(mShowFab ? View.VISIBLE : View.INVISIBLE);



if (!data.isKeynote()) {

showInScheduleDeferred(data.isInSchedule());

}

//...


updateTimeBasedUi(data);





}
private void displaySessionData(final SessionDetailModel data) {
//46 statement method inside a 900 line class




// Handle Keynote as a special case, where the user cannot remove it

// from the schedule (it is auto added to schedule on sync)

mShowFab = (AccountUtils.hasActiveAccount(getContext()) &&

!data.isKeynote());

mAddScheduleFab

.setVisibility(mShowFab ? View.VISIBLE : View.INVISIBLE);



if (!data.isKeynote()) {

showInScheduleDeferred(data.isInSchedule());

}

//...


updateTimeBasedUi(data);





}
private void displaySessionData(final SessionDetailModel data) {
//46 statement method inside a 900 line class




// Handle Keynote as a special case, where the user cannot remove it

// from the schedule (it is auto added to schedule on sync)

mShowFab = (AccountUtils.hasActiveAccount(getContext()) &&

!data.isKeynote());

mAddScheduleFab

.setVisibility(mShowFab ? View.VISIBLE : View.INVISIBLE);



if (!data.isKeynote()) {

showInScheduleDeferred(data.isInSchedule());

}

//...


updateTimeBasedUi(data);





}
@Test

public void onlyProvidesAddSessionToggleIfSessionIsNotKeynote()

throws Exception {

SessionDetailFragment sessionDetailFragment =

new SessionDetailFragment();

final SessionDetailModel sessionDetailModel =

mock(SessionDetailModel.class);

when(sessionDetailModel.isKeynote()).thenReturn(false);

sessionDetailFragment.displayData(sessionDetailModel,

SessionDetailModel.SessionDetailQueryEnum.SESSIONS);

final View addScheduleButton =

sessionDetailFragment.getView()

.findViewById(R.id.add_schedule_button);
assertTrue(addScheduleButton.getVisibility() == View.INVISIBLE);

}
@Test

public void onlyProvidesAddSessionToggleIfSessionIsNotKeynote()

throws Exception {

SessionDetailFragment sessionDetailFragment =

new SessionDetailFragment();

final SessionDetailModel sessionDetailModel =

mock(SessionDetailModel.class);

when(sessionDetailModel.isKeynote()).thenReturn(false);

sessionDetailFragment.displayData(sessionDetailModel,

SessionDetailModel.SessionDetailQueryEnum.SESSIONS);

final View addScheduleButton =

sessionDetailFragment.getView()

.findViewById(R.id.add_schedule_button);
assertTrue(addScheduleButton.getVisibility() == View.INVISIBLE);

}
@Test

public void onlyProvidesAddSessionToggleIfSessionIsNotKeynote()

throws Exception {

SessionDetailFragment sessionDetailFragment =

new SessionDetailFragment();

final SessionDetailModel sessionDetailModel =

mock(SessionDetailModel.class);

when(sessionDetailModel.isKeynote()).thenReturn(false);

sessionDetailFragment.displayData(sessionDetailModel,

SessionDetailModel.SessionDetailQueryEnum.SESSIONS);

final View addScheduleButton =

sessionDetailFragment.getView()

.findViewById(R.id.add_schedule_button);
assertTrue(addScheduleButton.getVisibility() == View.INVISIBLE);

}
@Test

public void onlyProvidesAddSessionToggleIfSessionIsNotKeynote()

throws Exception {

SessionDetailFragment sessionDetailFragment =

new SessionDetailFragment();

final SessionDetailModel sessionDetailModel =

mock(SessionDetailModel.class);

when(sessionDetailModel.isKeynote()).thenReturn(false);

sessionDetailFragment.displayData(sessionDetailModel,

SessionDetailModel.SessionDetailQueryEnum.SESSIONS);

final View addScheduleButton =

sessionDetailFragment.getView()

.findViewById(R.id.add_schedule_button);
assertTrue(addScheduleButton.getVisibility() == View.INVISIBLE);

}
@Test

public void onlyProvidesAddSessionToggleIfSessionIsNotKeynote()

throws Exception {

SessionDetailFragment sessionDetailFragment =

new SessionDetailFragment();

final SessionDetailModel sessionDetailModel =

mock(SessionDetailModel.class);

when(sessionDetailModel.isKeynote()).thenReturn(false);

sessionDetailFragment.onActivityCreated(null);
sessionDetailFragment.displayData(sessionDetailModel,

SessionDetailModel.SessionDetailQueryEnum.SESSIONS);

final View addScheduleButton =

sessionDetailFragment.getView()

.findViewById(R.id.add_schedule_button);
assertTrue(addScheduleButton.getVisibility() == View.INVISIBLE);

}
@Test

public void onlyProvidesAddSessionToggleIfSessionIsNotKeynote()

throws Exception {

SessionDetailFragment sessionDetailFragment =

new SessionDetailFragment();

final SessionDetailModel sessionDetailModel =

mock(SessionDetailModel.class);

when(sessionDetailModel.isKeynote()).thenReturn(false);

sessionDetailFragment.onActivityCreated(null);
sessionDetailFragment.onAttach(null);
sessionDetailFragment.displayData(sessionDetailModel,

SessionDetailModel.SessionDetailQueryEnum.SESSIONS);

final View addScheduleButton =

sessionDetailFragment.getView()

.findViewById(R.id.add_schedule_button);
assertTrue(addScheduleButton.getVisibility() == View.INVISIBLE);

}
SessionDetailFragment
has low cohesion
TDD Live
–Steve Freeman and Nat Pryce, 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.”
Activities tend to have tight
coupling and poor cohesion
This code is garbage
If Android Tests
Could Talk
https://twitter.com/philosohacker
https://twitter.com/unikeytech

More Related Content

More from K. Matthew Dupree

intro-to-metaprogramming-in-r.pdf
intro-to-metaprogramming-in-r.pdfintro-to-metaprogramming-in-r.pdf
intro-to-metaprogramming-in-r.pdf
K. Matthew Dupree
 
Intro To Gradient Descent in Javascript
Intro To Gradient Descent in JavascriptIntro To Gradient Descent in Javascript
Intro To Gradient Descent in Javascript
K. Matthew Dupree
 
Dagger 2, 2 years later
Dagger 2, 2 years laterDagger 2, 2 years later
Dagger 2, 2 years later
K. Matthew Dupree
 
An Introduction to RxJava
An Introduction to RxJavaAn Introduction to RxJava
An Introduction to RxJava
K. Matthew Dupree
 
Writing testable android apps
Writing testable android appsWriting testable android apps
Writing testable android apps
K. Matthew Dupree
 
Di and Dagger
Di and DaggerDi and Dagger
Di and Dagger
K. Matthew Dupree
 
Functional Testing for React Native Apps
Functional Testing for React Native AppsFunctional Testing for React Native Apps
Functional Testing for React Native Apps
K. Matthew Dupree
 
Testable android apps
Testable android appsTestable android apps
Testable android apps
K. Matthew Dupree
 

More from K. Matthew Dupree (8)

intro-to-metaprogramming-in-r.pdf
intro-to-metaprogramming-in-r.pdfintro-to-metaprogramming-in-r.pdf
intro-to-metaprogramming-in-r.pdf
 
Intro To Gradient Descent in Javascript
Intro To Gradient Descent in JavascriptIntro To Gradient Descent in Javascript
Intro To Gradient Descent in Javascript
 
Dagger 2, 2 years later
Dagger 2, 2 years laterDagger 2, 2 years later
Dagger 2, 2 years later
 
An Introduction to RxJava
An Introduction to RxJavaAn Introduction to RxJava
An Introduction to RxJava
 
Writing testable android apps
Writing testable android appsWriting testable android apps
Writing testable android apps
 
Di and Dagger
Di and DaggerDi and Dagger
Di and Dagger
 
Functional Testing for React Native Apps
Functional Testing for React Native AppsFunctional Testing for React Native Apps
Functional Testing for React Native Apps
 
Testable android apps
Testable android appsTestable android apps
Testable android apps
 

Recently uploaded

Apps Break Data
Apps Break DataApps Break Data
Apps Break Data
Ivo Velitchkov
 
Introduction of Cybersecurity with OSS at Code Europe 2024
Introduction of Cybersecurity with OSS  at Code Europe 2024Introduction of Cybersecurity with OSS  at Code Europe 2024
Introduction of Cybersecurity with OSS at Code Europe 2024
Hiroshi SHIBATA
 
GNSS spoofing via SDR (Criptored Talks 2024)
GNSS spoofing via SDR (Criptored Talks 2024)GNSS spoofing via SDR (Criptored Talks 2024)
GNSS spoofing via SDR (Criptored Talks 2024)
Javier Junquera
 
Biomedical Knowledge Graphs for Data Scientists and Bioinformaticians
Biomedical Knowledge Graphs for Data Scientists and BioinformaticiansBiomedical Knowledge Graphs for Data Scientists and Bioinformaticians
Biomedical Knowledge Graphs for Data Scientists and Bioinformaticians
Neo4j
 
Session 1 - Intro to Robotic Process Automation.pdf
Session 1 - Intro to Robotic Process Automation.pdfSession 1 - Intro to Robotic Process Automation.pdf
Session 1 - Intro to Robotic Process Automation.pdf
UiPathCommunity
 
inQuba Webinar Mastering Customer Journey Management with Dr Graham Hill
inQuba Webinar Mastering Customer Journey Management with Dr Graham HillinQuba Webinar Mastering Customer Journey Management with Dr Graham Hill
inQuba Webinar Mastering Customer Journey Management with Dr Graham Hill
LizaNolte
 
Christine's Supplier Sourcing Presentaion.pptx
Christine's Supplier Sourcing Presentaion.pptxChristine's Supplier Sourcing Presentaion.pptx
Christine's Supplier Sourcing Presentaion.pptx
christinelarrosa
 
Must Know Postgres Extension for DBA and Developer during Migration
Must Know Postgres Extension for DBA and Developer during MigrationMust Know Postgres Extension for DBA and Developer during Migration
Must Know Postgres Extension for DBA and Developer during Migration
Mydbops
 
5th LF Energy Power Grid Model Meet-up Slides
5th LF Energy Power Grid Model Meet-up Slides5th LF Energy Power Grid Model Meet-up Slides
5th LF Energy Power Grid Model Meet-up Slides
DanBrown980551
 
Connector Corner: Seamlessly power UiPath Apps, GenAI with prebuilt connectors
Connector Corner: Seamlessly power UiPath Apps, GenAI with prebuilt connectorsConnector Corner: Seamlessly power UiPath Apps, GenAI with prebuilt connectors
Connector Corner: Seamlessly power UiPath Apps, GenAI with prebuilt connectors
DianaGray10
 
Choosing The Best AWS Service For Your Website + API.pptx
Choosing The Best AWS Service For Your Website + API.pptxChoosing The Best AWS Service For Your Website + API.pptx
Choosing The Best AWS Service For Your Website + API.pptx
Brandon Minnick, MBA
 
Christine's Product Research Presentation.pptx
Christine's Product Research Presentation.pptxChristine's Product Research Presentation.pptx
Christine's Product Research Presentation.pptx
christinelarrosa
 
GraphRAG for LifeSciences Hands-On with the Clinical Knowledge Graph
GraphRAG for LifeSciences Hands-On with the Clinical Knowledge GraphGraphRAG for LifeSciences Hands-On with the Clinical Knowledge Graph
GraphRAG for LifeSciences Hands-On with the Clinical Knowledge Graph
Neo4j
 
"Scaling RAG Applications to serve millions of users", Kevin Goedecke
"Scaling RAG Applications to serve millions of users",  Kevin Goedecke"Scaling RAG Applications to serve millions of users",  Kevin Goedecke
"Scaling RAG Applications to serve millions of users", Kevin Goedecke
Fwdays
 
Monitoring and Managing Anomaly Detection on OpenShift.pdf
Monitoring and Managing Anomaly Detection on OpenShift.pdfMonitoring and Managing Anomaly Detection on OpenShift.pdf
Monitoring and Managing Anomaly Detection on OpenShift.pdf
Tosin Akinosho
 
Northern Engraving | Modern Metal Trim, Nameplates and Appliance Panels
Northern Engraving | Modern Metal Trim, Nameplates and Appliance PanelsNorthern Engraving | Modern Metal Trim, Nameplates and Appliance Panels
Northern Engraving | Modern Metal Trim, Nameplates and Appliance Panels
Northern Engraving
 
"Frontline Battles with DDoS: Best practices and Lessons Learned", Igor Ivaniuk
"Frontline Battles with DDoS: Best practices and Lessons Learned",  Igor Ivaniuk"Frontline Battles with DDoS: Best practices and Lessons Learned",  Igor Ivaniuk
"Frontline Battles with DDoS: Best practices and Lessons Learned", Igor Ivaniuk
Fwdays
 
“Temporal Event Neural Networks: A More Efficient Alternative to the Transfor...
“Temporal Event Neural Networks: A More Efficient Alternative to the Transfor...“Temporal Event Neural Networks: A More Efficient Alternative to the Transfor...
“Temporal Event Neural Networks: A More Efficient Alternative to the Transfor...
Edge AI and Vision Alliance
 
Mutation Testing for Task-Oriented Chatbots
Mutation Testing for Task-Oriented ChatbotsMutation Testing for Task-Oriented Chatbots
Mutation Testing for Task-Oriented Chatbots
Pablo GĂłmez Abajo
 
Essentials of Automations: Exploring Attributes & Automation Parameters
Essentials of Automations: Exploring Attributes & Automation ParametersEssentials of Automations: Exploring Attributes & Automation Parameters
Essentials of Automations: Exploring Attributes & Automation Parameters
Safe Software
 

Recently uploaded (20)

Apps Break Data
Apps Break DataApps Break Data
Apps Break Data
 
Introduction of Cybersecurity with OSS at Code Europe 2024
Introduction of Cybersecurity with OSS  at Code Europe 2024Introduction of Cybersecurity with OSS  at Code Europe 2024
Introduction of Cybersecurity with OSS at Code Europe 2024
 
GNSS spoofing via SDR (Criptored Talks 2024)
GNSS spoofing via SDR (Criptored Talks 2024)GNSS spoofing via SDR (Criptored Talks 2024)
GNSS spoofing via SDR (Criptored Talks 2024)
 
Biomedical Knowledge Graphs for Data Scientists and Bioinformaticians
Biomedical Knowledge Graphs for Data Scientists and BioinformaticiansBiomedical Knowledge Graphs for Data Scientists and Bioinformaticians
Biomedical Knowledge Graphs for Data Scientists and Bioinformaticians
 
Session 1 - Intro to Robotic Process Automation.pdf
Session 1 - Intro to Robotic Process Automation.pdfSession 1 - Intro to Robotic Process Automation.pdf
Session 1 - Intro to Robotic Process Automation.pdf
 
inQuba Webinar Mastering Customer Journey Management with Dr Graham Hill
inQuba Webinar Mastering Customer Journey Management with Dr Graham HillinQuba Webinar Mastering Customer Journey Management with Dr Graham Hill
inQuba Webinar Mastering Customer Journey Management with Dr Graham Hill
 
Christine's Supplier Sourcing Presentaion.pptx
Christine's Supplier Sourcing Presentaion.pptxChristine's Supplier Sourcing Presentaion.pptx
Christine's Supplier Sourcing Presentaion.pptx
 
Must Know Postgres Extension for DBA and Developer during Migration
Must Know Postgres Extension for DBA and Developer during MigrationMust Know Postgres Extension for DBA and Developer during Migration
Must Know Postgres Extension for DBA and Developer during Migration
 
5th LF Energy Power Grid Model Meet-up Slides
5th LF Energy Power Grid Model Meet-up Slides5th LF Energy Power Grid Model Meet-up Slides
5th LF Energy Power Grid Model Meet-up Slides
 
Connector Corner: Seamlessly power UiPath Apps, GenAI with prebuilt connectors
Connector Corner: Seamlessly power UiPath Apps, GenAI with prebuilt connectorsConnector Corner: Seamlessly power UiPath Apps, GenAI with prebuilt connectors
Connector Corner: Seamlessly power UiPath Apps, GenAI with prebuilt connectors
 
Choosing The Best AWS Service For Your Website + API.pptx
Choosing The Best AWS Service For Your Website + API.pptxChoosing The Best AWS Service For Your Website + API.pptx
Choosing The Best AWS Service For Your Website + API.pptx
 
Christine's Product Research Presentation.pptx
Christine's Product Research Presentation.pptxChristine's Product Research Presentation.pptx
Christine's Product Research Presentation.pptx
 
GraphRAG for LifeSciences Hands-On with the Clinical Knowledge Graph
GraphRAG for LifeSciences Hands-On with the Clinical Knowledge GraphGraphRAG for LifeSciences Hands-On with the Clinical Knowledge Graph
GraphRAG for LifeSciences Hands-On with the Clinical Knowledge Graph
 
"Scaling RAG Applications to serve millions of users", Kevin Goedecke
"Scaling RAG Applications to serve millions of users",  Kevin Goedecke"Scaling RAG Applications to serve millions of users",  Kevin Goedecke
"Scaling RAG Applications to serve millions of users", Kevin Goedecke
 
Monitoring and Managing Anomaly Detection on OpenShift.pdf
Monitoring and Managing Anomaly Detection on OpenShift.pdfMonitoring and Managing Anomaly Detection on OpenShift.pdf
Monitoring and Managing Anomaly Detection on OpenShift.pdf
 
Northern Engraving | Modern Metal Trim, Nameplates and Appliance Panels
Northern Engraving | Modern Metal Trim, Nameplates and Appliance PanelsNorthern Engraving | Modern Metal Trim, Nameplates and Appliance Panels
Northern Engraving | Modern Metal Trim, Nameplates and Appliance Panels
 
"Frontline Battles with DDoS: Best practices and Lessons Learned", Igor Ivaniuk
"Frontline Battles with DDoS: Best practices and Lessons Learned",  Igor Ivaniuk"Frontline Battles with DDoS: Best practices and Lessons Learned",  Igor Ivaniuk
"Frontline Battles with DDoS: Best practices and Lessons Learned", Igor Ivaniuk
 
“Temporal Event Neural Networks: A More Efficient Alternative to the Transfor...
“Temporal Event Neural Networks: A More Efficient Alternative to the Transfor...“Temporal Event Neural Networks: A More Efficient Alternative to the Transfor...
“Temporal Event Neural Networks: A More Efficient Alternative to the Transfor...
 
Mutation Testing for Task-Oriented Chatbots
Mutation Testing for Task-Oriented ChatbotsMutation Testing for Task-Oriented Chatbots
Mutation Testing for Task-Oriented Chatbots
 
Essentials of Automations: Exploring Attributes & Automation Parameters
Essentials of Automations: Exploring Attributes & Automation ParametersEssentials of Automations: Exploring Attributes & Automation Parameters
Essentials of Automations: Exploring Attributes & Automation Parameters
 

If Android Tests Could Talk

  • 1. If Android Tests Could Talk Matt Dupree
  • 2. Their pace has quickened
  • 3. This code is garbage
  • 4. –Steve Freeman and Nat Pryce, 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.”
  • 5. –Steve Freeman and Nat Pryce, 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.”
  • 6. –Steve Freeman and Nat Pryce, 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.”
  • 7. Activities tend to have tight coupling and poor cohesion
  • 8. Application components (activities, services, providers, receivers) are interfaces for your application to interact with the operating system; don’t take them as a recommendation of the facilities you should architect your entire application around. —Chet Haase, Developing for Android VII
  • 9. Activities tend to have tight coupling and poor cohesion
  • 10.
  • 11. @Override
 public void onRequestPermissionsResult(final int requestCode,
 @NonNull final String[] permissions,
 @NonNull final int[] grantResults) {
 
 if (requestCode != REQUEST_LOCATION_PERMISSION) {
 return;
 }
 
 if (permissions.length == 1 &&
 LOCATION_PERMISSION.equals(permissions[0]) &&
 grantResults[0] == PackageManager.PERMISSION_GRANTED) {
 // Permission has been granted.
 if (mMapFragment != null) {
 mMapFragment.setMyLocationEnabled(true);
 }
 } else {
 // Permission was denied. Display error message.
 Toast.makeText(this, R.string.map_permission_denied,
 Toast.LENGTH_SHORT).show();
 }
 super.onRequestPermissionsResult(requestCode, permissions,
 grantResults);
 }
  • 12. @Override
 public void onRequestPermissionsResult(final int requestCode,
 @NonNull final String[] permissions,
 @NonNull final int[] grantResults) {
 
 if (requestCode != REQUEST_LOCATION_PERMISSION) {
 return;
 }
 
 if (permissions.length == 1 &&
 LOCATION_PERMISSION.equals(permissions[0]) &&
 grantResults[0] == PackageManager.PERMISSION_GRANTED) {
 // Permission has been granted.
 if (mMapFragment != null) {
 mMapFragment.setMyLocationEnabled(true);
 }
 } else {
 // Permission was denied. Display error message.
 Toast.makeText(this, R.string.map_permission_denied,
 Toast.LENGTH_SHORT).show();
 }
 super.onRequestPermissionsResult(requestCode, permissions,
 grantResults);
 }
  • 13. @Override
 public void onRequestPermissionsResult(final int requestCode,
 @NonNull final String[] permissions,
 @NonNull final int[] grantResults) {
 
 if (requestCode != REQUEST_LOCATION_PERMISSION) {
 return;
 }
 
 if (permissions.length == 1 &&
 LOCATION_PERMISSION.equals(permissions[0]) &&
 grantResults[0] == PackageManager.PERMISSION_GRANTED) {
 // Permission has been granted.
 if (mMapFragment != null) {
 mMapFragment.setMyLocationEnabled(true);
 }
 } else {
 // Permission was denied. Display error message.
 Toast.makeText(this, R.string.map_permission_denied,
 Toast.LENGTH_SHORT).show();
 }
 super.onRequestPermissionsResult(requestCode, permissions,
 grantResults);
 }
  • 14. @Override
 public void onRequestPermissionsResult(final int requestCode,
 @NonNull final String[] permissions,
 @NonNull final int[] grantResults) {
 
 if (requestCode != REQUEST_LOCATION_PERMISSION) {
 return;
 }
 
 if (permissions.length == 1 &&
 LOCATION_PERMISSION.equals(permissions[0]) &&
 grantResults[0] == PackageManager.PERMISSION_GRANTED) {
 // Permission has been granted.
 if (mMapFragment != null) {
 mMapFragment.setMyLocationEnabled(true);
 }
 } else {
 // Permission was denied. Display error message.
 Toast.makeText(this, R.string.map_permission_denied,
 Toast.LENGTH_SHORT).show();
 }
 super.onRequestPermissionsResult(requestCode, permissions,
 grantResults);
 }
  • 15. @Override
 public void onRequestPermissionsResult(final int requestCode,
 @NonNull final String[] permissions,
 @NonNull final int[] grantResults) {
 
 if (requestCode != REQUEST_LOCATION_PERMISSION) {
 return;
 }
 
 if (permissions.length == 1 &&
 LOCATION_PERMISSION.equals(permissions[0]) &&
 grantResults[0] == PackageManager.PERMISSION_GRANTED) {
 // Permission has been granted.
 if (mMapFragment != null) {
 mMapFragment.setMyLocationEnabled(true);
 }
 } else {
 // Permission was denied. Display error message.
 Toast.makeText(this, R.string.map_permission_denied,
 Toast.LENGTH_SHORT).show();
 }
 super.onRequestPermissionsResult(requestCode, permissions,
 grantResults);
 }
  • 16.
  • 17. @Test
 public void showsToastIfPermissionIsRejected()
 throws Exception {
 MapActivity mapActivity = new MapActivity();
 
 mapActivity.onRequestPermissionsResult(
 MapActivity.REQUEST_LOCATION_PERMISSION,
 new String[]{MapActivity.LOCATION_PERMISSION}, new int[]{
 PackageManager.PERMISSION_DENIED});
 
 assertToastDisplayed();
 }
  • 18. @Test
 public void showsToastIfPermissionIsRejected()
 throws Exception {
 MapActivity mapActivity = new MapActivity();
 
 mapActivity.onRequestPermissionsResult(
 MapActivity.REQUEST_LOCATION_PERMISSION,
 new String[]{MapActivity.LOCATION_PERMISSION}, new int[]{
 PackageManager.PERMISSION_DENIED});
 
 assertToastDisplayed();
 }
  • 19. @Test
 public void showsToastIfPermissionIsRejected()
 throws Exception {
 MapActivity mapActivity = new MapActivity();
 
 mapActivity.onRequestPermissionsResult(
 MapActivity.REQUEST_LOCATION_PERMISSION,
 new String[]{MapActivity.LOCATION_PERMISSION}, new int[]{
 PackageManager.PERMISSION_DENIED});
 
 assertToastDisplayed();
 }
  • 20. @Test
 public void showsToastIfPermissionIsRejected()
 throws Exception {
 MapActivity mapActivity = new MapActivity();
 
 mapActivity.onRequestPermissionsResult(
 MapActivity.REQUEST_LOCATION_PERMISSION,
 new String[]{MapActivity.LOCATION_PERMISSION}, new int[]{
 PackageManager.PERMISSION_DENIED});
 
 assertToastDisplayed();
 }
  • 21. private void assertToastDisplayed() {
 // It can't be done, sir 
 }
  • 22. @Override
 public void onRequestPermissionsResult(final int requestCode,
 @NonNull final String[] permissions,
 @NonNull final int[] grantResults) {
 
 if (requestCode != REQUEST_LOCATION_PERMISSION) {
 return;
 }
 
 if (permissions.length == 1 &&
 LOCATION_PERMISSION.equals(permissions[0]) &&
 grantResults[0] == PackageManager.PERMISSION_GRANTED) {
 // Permission has been granted.
 if (mMapFragment != null) {
 mMapFragment.setMyLocationEnabled(true);
 }
 } else {
 // Permission was denied. Display error message.
 Toast.makeText(this, R.string.map_permission_denied,
 Toast.LENGTH_SHORT).show();
 }
 super.onRequestPermissionsResult(requestCode, permissions,
 grantResults);
 }
  • 23.
  • 24.
  • 25.
  • 26.
  • 27. MapActivity is tightly coupled with MapFragment and Toast
  • 28. static class OnPermissionResultListener { 
 private final PermittedView mPermittedView;
 
 void onPermissionResult(final int requestCode,
 final String[] permissions, final int[] grantResults) {}
 
 interface PermittedView { 
 void displayPermissionDenied(); void displayPermittedView();
 }
 }
  • 29. static class OnPermissionResultListener { 
 private final PermittedView mPermittedView;
 
 void onPermissionResult(final int requestCode,
 final String[] permissions, final int[] grantResults) {}
 
 interface PermittedView { 
 void displayPermissionDenied(); void displayPermittedView();
 }
 }
  • 30. static class OnPermissionResultListener { 
 private final PermittedView mPermittedView;
 
 void onPermissionResult(final int requestCode,
 final String[] permissions, final int[] grantResults) {}
 
 interface PermittedView { 
 void displayPermissionDenied(); void displayPermittedView();
 }
 }
  • 31. void onPermissionResult(final int requestCode,
 final String[] permissions, final int[] grantResults) {
 if (requestCode != MapActivity.REQUEST_LOCATION_PERMISSION) {
 return;
 }
 
 if (permissions.length == 1 &&
 MapActivity.LOCATION_PERMISSION.equals(permissions[0]) &&
 grantResults[0] == PackageManager.PERMISSION_GRANTED) {
 // Permission has been granted.
 mPermittedView.displayPermittedView();
 
 } else {
 // Permission was denied. Display error message.
 mPermittedView.displayPermissionDenied();
 }
 }
  • 32. void onPermissionResult(final int requestCode,
 final String[] permissions, final int[] grantResults) {
 if (requestCode != MapActivity.REQUEST_LOCATION_PERMISSION) {
 return;
 }
 
 if (permissions.length == 1 &&
 MapActivity.LOCATION_PERMISSION.equals(permissions[0]) &&
 grantResults[0] == PackageManager.PERMISSION_GRANTED) {
 // Permission has been granted.
 mPermittedView.displayPermittedView();
 
 } else {
 // Permission was denied. Display error message.
 mPermittedView.displayPermissionDenied();
 }
 }
  • 33. @Test
 public void displaysErrorWhenPermissionRejected() throws Exception {
 
 OnPermissionResultListener onPermissionResultListener =
 new OnPermissionResultListener(mPermittedView);
 
 onPermissionResultListener.onPermissionResult(
 MapActivity.REQUEST_LOCATION_PERMISSION,
 new String[]{MapActivity.LOCATION_PERMISSION},
 new int[]{PackageManager.PERMISSION_DENIED});
 
 verify(mPermittedView).displayPermissionDenied();
 }
  • 34. @Test
 public void displaysErrorWhenPermissionRejected() throws Exception {
 
 OnPermissionResultListener onPermissionResultListener =
 new OnPermissionResultListener(mPermittedView);
 
 onPermissionResultListener.onPermissionResult(
 MapActivity.REQUEST_LOCATION_PERMISSION,
 new String[]{MapActivity.LOCATION_PERMISSION},
 new int[]{PackageManager.PERMISSION_DENIED});
 
 verify(mPermittedView).displayPermissionDenied();
 }
  • 35. @Test
 public void displaysErrorWhenPermissionRejected() throws Exception {
 
 OnPermissionResultListener onPermissionResultListener =
 new OnPermissionResultListener(mPermittedView);
 
 onPermissionResultListener.onPermissionResult(
 MapActivity.REQUEST_LOCATION_PERMISSION,
 new String[]{MapActivity.LOCATION_PERMISSION},
 new int[]{PackageManager.PERMISSION_DENIED});
 
 verify(mPermittedView).displayPermissionDenied();
 }
  • 36. @Test
 public void displaysErrorWhenPermissionRejected() throws Exception {
 
 OnPermissionResultListener onPermissionResultListener =
 new OnPermissionResultListener(mPermittedView);
 
 onPermissionResultListener.onPermissionResult(
 MapActivity.REQUEST_LOCATION_PERMISSION,
 new String[]{MapActivity.LOCATION_PERMISSION},
 new int[]{PackageManager.PERMISSION_DENIED});
 
 verify(mPermittedView).displayPermissionDenied();
 }
  • 38. –Edward Yourdon and Larry Constantine “what we are striving for is loosely coupled systems…in which one can study (or debug, or maintain) any one module without having to know very much about any other modules in the system”
  • 39.
  • 40. @Override
 public void displayPermissionDenied() {
 Toast.makeText(MapActivity.this, R.string.map_permission_denied,
 Toast.LENGTH_SHORT).show();
 }
  • 41. @Override
 public void displayPermissionDenied() {
 PermissionsUtils.displayConditionalPermissionDenialSnackbar(this,
 R.string.map_permission_denied, PERMISSIONS,
 REQUEST_LOCATION_PERMISSION, false);
 }
  • 42. –Kent Beck, TDD By Example “Dependency is the key problem in software development at all scales…if dependency is the problem, duplication is the symptom.”
  • 43.
  • 44. @Override
 public void onRequestPermissionsResult(final int requestCode,
 @NonNull final String[] permissions,
 @NonNull final int[] grantResults) {
 
 if (grantResults.length == APP_REQUIRED_PERMISSIONS.length &&
 grantResults[0] == PackageManager.PERMISSION_GRANTED &&
 grantResults[1] == PackageManager.PERMISSION_GRANTED) {
 // If permission granted then refresh account list so user can
 // select an account.
 clearSnackbar();
 reloadAccounts();
 refreshAccountListUI();
 } else {
 LOGI(TAG, "onRequestPermissionResult with permissions denied");
 mSnackbar = new WeakReference<>(PermissionsUtils
 .displayConditionalPermissionDenialSnackbar(
 getActivity(),
 R.string.welcome_permissions_rationale,
 APP_REQUIRED_PERMISSIONS,
 REQUEST_PERMISSION_REQUEST_CODE));
 }
 }
  • 45. Activities tend to have tight coupling and poor cohesion
  • 46. –Steve Freeman and Nat Pryce, 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.”
  • 47. Activities tend to have tight coupling and poor cohesion
  • 48. –Steve Freeman and Nat Pryce, 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.”
  • 49.
  • 50. private void displaySessionData(final SessionDetailModel data) { //46 statement method inside a 900 line class 
 
 // Handle Keynote as a special case, where the user cannot remove it
 // from the schedule (it is auto added to schedule on sync)
 mShowFab = (AccountUtils.hasActiveAccount(getContext()) &&
 !data.isKeynote());
 mAddScheduleFab
 .setVisibility(mShowFab ? View.VISIBLE : View.INVISIBLE);
 
 if (!data.isKeynote()) {
 showInScheduleDeferred(data.isInSchedule());
 }
 //... 
 updateTimeBasedUi(data);
 
 
 }
  • 51. private void displaySessionData(final SessionDetailModel data) { //46 statement method inside a 900 line class! 
 
 // Handle Keynote as a special case, where the user cannot remove it
 // from the schedule (it is auto added to schedule on sync)
 mShowFab = (AccountUtils.hasActiveAccount(getContext()) &&
 !data.isKeynote());
 mAddScheduleFab
 .setVisibility(mShowFab ? View.VISIBLE : View.INVISIBLE);
 
 if (!data.isKeynote()) {
 showInScheduleDeferred(data.isInSchedule());
 }
 //... 
 updateTimeBasedUi(data);
 
 
 }
  • 52. private void displaySessionData(final SessionDetailModel data) { //46 statement method inside a 900 line class 
 
 // Handle Keynote as a special case, where the user cannot remove it
 // from the schedule (it is auto added to schedule on sync)
 mShowFab = (AccountUtils.hasActiveAccount(getContext()) &&
 !data.isKeynote());
 mAddScheduleFab
 .setVisibility(mShowFab ? View.VISIBLE : View.INVISIBLE);
 
 if (!data.isKeynote()) {
 showInScheduleDeferred(data.isInSchedule());
 }
 //... 
 updateTimeBasedUi(data);
 
 
 }
  • 53. private void displaySessionData(final SessionDetailModel data) { //46 statement method inside a 900 line class 
 
 // Handle Keynote as a special case, where the user cannot remove it
 // from the schedule (it is auto added to schedule on sync)
 mShowFab = (AccountUtils.hasActiveAccount(getContext()) &&
 !data.isKeynote());
 mAddScheduleFab
 .setVisibility(mShowFab ? View.VISIBLE : View.INVISIBLE);
 
 if (!data.isKeynote()) {
 showInScheduleDeferred(data.isInSchedule());
 }
 //... 
 updateTimeBasedUi(data);
 
 
 }
  • 54. private void displaySessionData(final SessionDetailModel data) { //46 statement method inside a 900 line class 
 
 // Handle Keynote as a special case, where the user cannot remove it
 // from the schedule (it is auto added to schedule on sync)
 mShowFab = (AccountUtils.hasActiveAccount(getContext()) &&
 !data.isKeynote());
 mAddScheduleFab
 .setVisibility(mShowFab ? View.VISIBLE : View.INVISIBLE);
 
 if (!data.isKeynote()) {
 showInScheduleDeferred(data.isInSchedule());
 }
 //... 
 updateTimeBasedUi(data);
 
 
 }
  • 55.
  • 56. @Test
 public void onlyProvidesAddSessionToggleIfSessionIsNotKeynote()
 throws Exception {
 SessionDetailFragment sessionDetailFragment =
 new SessionDetailFragment();
 final SessionDetailModel sessionDetailModel =
 mock(SessionDetailModel.class);
 when(sessionDetailModel.isKeynote()).thenReturn(false);
 sessionDetailFragment.displayData(sessionDetailModel,
 SessionDetailModel.SessionDetailQueryEnum.SESSIONS);
 final View addScheduleButton =
 sessionDetailFragment.getView()
 .findViewById(R.id.add_schedule_button); assertTrue(addScheduleButton.getVisibility() == View.INVISIBLE);
 }
  • 57. @Test
 public void onlyProvidesAddSessionToggleIfSessionIsNotKeynote()
 throws Exception {
 SessionDetailFragment sessionDetailFragment =
 new SessionDetailFragment();
 final SessionDetailModel sessionDetailModel =
 mock(SessionDetailModel.class);
 when(sessionDetailModel.isKeynote()).thenReturn(false);
 sessionDetailFragment.displayData(sessionDetailModel,
 SessionDetailModel.SessionDetailQueryEnum.SESSIONS);
 final View addScheduleButton =
 sessionDetailFragment.getView()
 .findViewById(R.id.add_schedule_button); assertTrue(addScheduleButton.getVisibility() == View.INVISIBLE);
 }
  • 58. @Test
 public void onlyProvidesAddSessionToggleIfSessionIsNotKeynote()
 throws Exception {
 SessionDetailFragment sessionDetailFragment =
 new SessionDetailFragment();
 final SessionDetailModel sessionDetailModel =
 mock(SessionDetailModel.class);
 when(sessionDetailModel.isKeynote()).thenReturn(false);
 sessionDetailFragment.displayData(sessionDetailModel,
 SessionDetailModel.SessionDetailQueryEnum.SESSIONS);
 final View addScheduleButton =
 sessionDetailFragment.getView()
 .findViewById(R.id.add_schedule_button); assertTrue(addScheduleButton.getVisibility() == View.INVISIBLE);
 }
  • 59. @Test
 public void onlyProvidesAddSessionToggleIfSessionIsNotKeynote()
 throws Exception {
 SessionDetailFragment sessionDetailFragment =
 new SessionDetailFragment();
 final SessionDetailModel sessionDetailModel =
 mock(SessionDetailModel.class);
 when(sessionDetailModel.isKeynote()).thenReturn(false);
 sessionDetailFragment.displayData(sessionDetailModel,
 SessionDetailModel.SessionDetailQueryEnum.SESSIONS);
 final View addScheduleButton =
 sessionDetailFragment.getView()
 .findViewById(R.id.add_schedule_button); assertTrue(addScheduleButton.getVisibility() == View.INVISIBLE);
 }
  • 60.
  • 61. @Test
 public void onlyProvidesAddSessionToggleIfSessionIsNotKeynote()
 throws Exception {
 SessionDetailFragment sessionDetailFragment =
 new SessionDetailFragment();
 final SessionDetailModel sessionDetailModel =
 mock(SessionDetailModel.class);
 when(sessionDetailModel.isKeynote()).thenReturn(false);
 sessionDetailFragment.onActivityCreated(null); sessionDetailFragment.displayData(sessionDetailModel,
 SessionDetailModel.SessionDetailQueryEnum.SESSIONS);
 final View addScheduleButton =
 sessionDetailFragment.getView()
 .findViewById(R.id.add_schedule_button); assertTrue(addScheduleButton.getVisibility() == View.INVISIBLE);
 }
  • 62. @Test
 public void onlyProvidesAddSessionToggleIfSessionIsNotKeynote()
 throws Exception {
 SessionDetailFragment sessionDetailFragment =
 new SessionDetailFragment();
 final SessionDetailModel sessionDetailModel =
 mock(SessionDetailModel.class);
 when(sessionDetailModel.isKeynote()).thenReturn(false);
 sessionDetailFragment.onActivityCreated(null); sessionDetailFragment.onAttach(null); sessionDetailFragment.displayData(sessionDetailModel,
 SessionDetailModel.SessionDetailQueryEnum.SESSIONS);
 final View addScheduleButton =
 sessionDetailFragment.getView()
 .findViewById(R.id.add_schedule_button); assertTrue(addScheduleButton.getVisibility() == View.INVISIBLE);
 }
  • 65. –Steve Freeman and Nat Pryce, 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.”
  • 66. Activities tend to have tight coupling and poor cohesion
  • 67. This code is garbage
  • 68. If Android Tests Could Talk https://twitter.com/philosohacker https://twitter.com/unikeytech