SlideShare a Scribd company logo
1 of 58
Download to read offline
Testable Android Apps
using data binding and MVVM
Fabio Collini
GDG DevFest – Milano – October 2015 – @fabioCollini 2
Ego slide
@fabioCollini
linkedin.com/in/fabiocollini
Folder Organizer
cosenonjaviste.it
nana bianca
Freapp
instal.com
Rain tomorrow?
GDG DevFest – Milano – October 2015 – @fabioCollini 3
Agenda
1. ROI and Legacy code
2. Model View ViewModel
3. JVM Unit tests
4. Mockito
5. Espresso
GDG DevFest - Milano - October 2015 - @fabioCollini 4
1ROI and legacy code
GDG DevFest – Milano – October 2015 – @fabioCollini 5
Quick survey
Do you write automated tests?
GDG DevFest – Milano – October 2015 – @fabioCollini 6
GDG DevFest – Milano – October 2015 – @fabioCollini 7
Return of Investment - ROI
Net profit
Investment
GDG DevFest – Milano – October 2015 – @fabioCollini 8
Legacy code
Edit and pray
Vs
Cover and modify
Legacy code is code
without unit tests
GDG DevFest – Milano – October 2015 – @fabioCollini 9
Test After Development
Write the feature implementation
Do some manual testing
Try to write automatic tests
Modify the initial implementation to test it
“Standard” Android code is not testable :(
GDG DevFest – Milano – October 2015 – @fabioCollini 10
Legacy code dilemma
When we change code,
we should have tests in place.
To put tests in place,
we often have to change code.
Michael Feathers
GDG DevFest - Milano - October 2015 - @fabioCollini 11
2Model View ViewModel
GDG DevFest – Milano – October 2015 – @fabioCollini 12
Testable code
Data binding and MVVM
GDG DevFest – Milano – October 2015 – @fabioCollini 13
Model View ViewModel
View
ViewModel
Model
DataBinding
GDG DevFest – Milano – October 2015 – @fabioCollini 14
Android Model View ViewModel
View
ViewModel
Model
DataBinding
Retained on
configuration change
Saved in Activity or
Fragment state
Activity or Fragment
GDG DevFest – Milano – October 2015 – @fabioCollini 15
mv2m
https://github.com/fabioCollini/mv2m
GDG DevFest – Milano – October 2015 – @fabioCollini 16
NoteActivity
NoteViewModel
NoteModel
note_detail.xml
NoteDetailBinding
DataBinding
GDG DevFest – Milano – October 2015 – @fabioCollini 17
View ViewModel RetrofitService
onClick
update
binding
Model
View ViewModel RetrofitServiceModel
request
response
binding
GDG DevFest – Milano – October 2015 – @fabioCollini 18
NoteModel
Saved on Activity state
public class NoteModel implements Parcelable {



private long noteId;



private ObservableBoolean error = new ObservableBoolean();



private ObservableString title = new ObservableString();



private ObservableString text = new ObservableString();



private ObservableInt titleError = new ObservableInt();



private ObservableInt textError = new ObservableInt();



//...

}
GDG DevFest – Milano – October 2015 – @fabioCollini 19
NoteActivity
public class NoteActivity extends
ViewModelActivity<NoteViewModel> {



@Override public NoteViewModel createViewModel() {

return new NoteViewModel(/* .. */);

}



@Override protected void onCreate(Bundle state) {

super.onCreate(state);

NoteDetailBinding binding =
DataBindingUtil.setContentView(this,
R.layout.note_detail);

binding.setViewModel(viewModel);

}

}
GDG DevFest – Milano – October 2015 – @fabioCollini 20
note_detail.xml
<?xml version="1.0" encoding="utf-8"?>

<layout
xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:app="http://schemas.android.com/apk/res-auto">



<data>

<variable

name="viewModel"

type="it.cosenonjaviste.core.NoteViewModel"/>

</data>



<FrameLayout

android:layout_width="match_parent"

android:layout_height="match_parent">



<!-- ... -->



</FrameLayout>

</layout>
GDG DevFest – Milano – October 2015 – @fabioCollini 21
note_detail.xml
<LinearLayout app:visible="@{viewModel.loading}">

<ProgressBar />

<TextView />

</LinearLayout>



<LinearLayout app:visible=“@{viewModel.model.error}">


<TextView android:text="@string/error_loading_note"/>



<Button android:text=“@string/retry"/>

</LinearLayout>



<ScrollView app:visible="@{!viewModel.loading &amp;&amp;
!viewModel.model.error}">

<!-- ... -->

</ScrollView>
GDG DevFest – Milano – October 2015 – @fabioCollini 22
note_detail.xml
<LinearLayout>

<android.support.design.widget.TextInputLayout

app:error=“@{viewModel.model.titleError}">


<EditText app:binding=“@{viewModel.model.title}" />


</android.support.design.widget.TextInputLayout>

<!-- ... -->
<RelativeLayout>


<Button
android:enabled="@{!viewModel.sending}"

app:onClick="@{viewModel.save}"/>

<ProgressBar app:visible=“@{viewModel.sending}" />

</RelativeLayout>

</LinearLayout>
GDG DevFest – Milano – October 2015 – @fabioCollini 23
app:binding
@BindingAdapter({"app:binding"})

public static void bindEditText(EditText view,
final ObservableString observableString) {

if (view.getTag(R.id.binded) == null) {

view.setTag(R.id.binded, true);

view.addTextChangedListener(new TextWatcherAdapter() {

@Override public void onTextChanged(
CharSequence s, int st, int b, int c) {

observableString.set(s.toString());

}

});

}

String newValue = observableString.get();

if (!view.getText().toString().equals(newValue)) {

view.setText(newValue);

}

}
GDG DevFest – Milano – October 2015 – @fabioCollini 24
app:visible app:onClick
@BindingAdapter({"app:visible"})

public static void bindVisible(View view, boolean b) {

view.setVisibility(b ? View.VISIBLE : View.INVISIBLE);

}



@BindingAdapter({"app:onClick"})

public static void bindOnClick(View view,
final Runnable listener) {

view.setOnClickListener(new View.OnClickListener() {

@Override public void onClick(View v) {

listener.run();

}

});

}
GDG DevFest – Milano – October 2015 – @fabioCollini 25
NoteViewModel
public class NoteViewModel extends ViewModel<NoteModel> {



//...



@Override public NoteModel createDefaultModel() {

return new NoteModel();

}



@Override public void resume() {

if (!getModel().isLoaded()) {

reloadData();

}

}



public void reloadData() {

}



//...

}
GDG DevFest - Milano - October 2015 - @fabioCollini 26
3JVM Unit tests
GDG DevFest – Milano – October 2015 – @fabioCollini 27
Instrumentation tests
run on a device (real or emulated)
high code coverage
Vs
JVM tests
fast
low code coverage
GDG DevFest – Milano – October 2015 – @fabioCollini
JVM Test
28
NoteActivity
NoteViewModel
NoteModel
note_detail.xml
NoteDetailBinding
DataBinding
GDG DevFest – Milano – October 2015 – @fabioCollini 29
NoteViewModel.reloadData
public class NoteViewModel extends ViewModel<NoteModel> {



//...

@Override public void resume() {

if (!getModel().isLoaded()) {

reloadData();

}

}



public void reloadData() {

try {

Note note = NoteLoader.singleton().load();

getModel().update(note);

} catch (Exception e) {

getModel().getError().set(true);

}

}

//...

}
GDG DevFest – Milano – October 2015 – @fabioCollini 30
First test
AssertJ
@Test

public void testLoadData() {

NoteViewModel viewModel = new NoteViewModel();



NoteModel model = viewModel.initAndResume();



assertThat(model.getTitle().get()).isEqualTo("???");

assertThat(model.getText().get()).isEqualTo("???");

assertThat(model.getError().get()).isFalse();

}
GDG DevFest – Milano – October 2015 – @fabioCollini 31
NoteLoader.singleton
public class NoteViewModel extends ViewModel<NoteModel> {



//...

@Override public void resume() {

if (!getModel().isLoaded()) {

reloadData();

}

}



public void reloadData() {

try {

Note note = NoteLoader.singleton().load();

getModel().update(note);

} catch (Exception e) {

getModel().getError().set(true);

}

}

//...

}
GDG DevFest – Milano – October 2015 – @fabioCollini 32
Dependency Injection
public class NoteViewModel
extends ViewModel<NoteModel, NoteView> {
private NoteLoader noteLoader;

public NoteViewModel(NoteLoader noteLoader) {

this.noteLoader = noteLoader;

}


public void reloadData() {

try {

Note note = noteLoader.load();

getModel().update(note);

} catch (Exception e) {

getModel().getError().set(true);

}

}
//...
}
GDG DevFest – Milano – October 2015 – @fabioCollini 33
NoteLoaderStub
public class NoteLoaderStub implements NoteLoader {


private Note note;



public NoteLoaderStub(Note note) {

this.note = note;

}



@Override public Note load() {

return note;

}

}
GDG DevFest – Milano – October 2015 – @fabioCollini 34
Test with stub
@Test

public void testLoadData() {

NoteLoaderStub stub =
new NoteLoaderStub(new Note(1, "a", "b"));


NoteViewModel viewModel = new NoteViewModel(stub);



NoteModel model = viewModel.initAndResume();



assertThat(model.getTitle().get()).isEqualTo("a");

assertThat(model.getText().get()).isEqualTo("b");

assertThat(model.getError().get()).isFalse();

}
GDG DevFest – Milano – October 2015 – @fabioCollini
public void save() {

NoteModel model = getModel();

boolean titleValid = checkMandatory(
model.getTitle(), model.getTitleError());

boolean textValid = checkMandatory(
model.getText(), model.getTextError());

if (titleValid && textValid) {

try {

noteSaver.save(
model.getNoteId(),
model.getTitle().get(),
model.getText().get());
messageManager.showMessage(R.string.note_saved);

} catch (RetrofitError e) {

messageManager.showMessage(
R.string.error_saving_note);

}

}

}
35
save method
Dependency Injection
Dependency Injection
GDG DevFest – Milano – October 2015 – @fabioCollini 36
SnackbarMessageManager
public class SnackbarMessageManager implements MessageManager {

private Activity activity;



@Override public void showMessage(int message) {

if (activity != null) {

Snackbar.make(
activity.findViewById(android.R.id.content),
message,
Snackbar.LENGTH_LONG
).show();

}

}



@Override public void setActivity(Activity activity) {

this.activity = activity;

}

}
GDG DevFest – Milano – October 2015 – @fabioCollini 37
MessageManagerSpy
public class MessageManagerSpy implements MessageManager {

public int message;



@Override public void showMessage(int message) {

this.message = message;

}



@Override public void setActivity(Activity activity) {

}

}
GDG DevFest – Milano – October 2015 – @fabioCollini 38
NoteSaverSpy
public class NoteSaverSpy implements NoteSaver {



public long id;

public String title;

public String text;



@Override public Response save(
long id, String title, String text) {

this.id = id;

this.title = title;

this.text = text;

return null;

}

}
GDG DevFest – Milano – October 2015 – @fabioCollini 39
Test with spy
@Test

public void testSaveData() {

NoteLoaderStub stub =
new NoteLoaderStub(new Note(1, "a", "b"));
NoteSaverSpy saverSpy = new NoteSaverSpy();

MessageManagerSpy messageSpy = new MessageManagerSpy();
NoteViewModel viewModel = new NoteViewModel(
stub, saverSpy, messageSpy);


NoteModel model = viewModel.initAndResume();

model.getTitle().set("newTitle");

model.getText().set("newText");

viewModel.save();



assertThat(saverSpy.id).isEqualTo(1L);

assertThat(saverSpy.title).isEqualTo("newTitle");

assertThat(saverSpy.text).isEqualTo("newText");
assertThat(messageSpy.message)
.isEqualTo(R.string.note_saved);

}
GDG DevFest - Milano - October 2015 - @fabioCollini 40
4Mockito
GDG DevFest – Milano – October 2015 – @fabioCollini 41
Mockito
@Test

public void testLoadData() {

NoteLoader noteLoader =

Mockito.mock(NoteLoader.class);

NoteSaver noteSaver = 

Mockito.mock(NoteSaver.class);
MessageManager messageManager =
Mockito.mock(MessageManager.class);


NoteViewModel viewModel = new NoteViewModel(

noteLoader, noteSaver, messageManager);



when(noteLoader.load())

.thenReturn(new Note(123, "title", "text"));

NoteModel model = viewModel.initAndResume();



assertThat(model.getTitle().get()).isEqualTo("title");

assertThat(model.getText().get()).isEqualTo("text");

}
GDG DevFest – Milano – October 2015 – @fabioCollini
MockLoader
MockLoaderNoteLoader
NoteLoader
42
ViewModel
initAndResume
update
Model
request
response
JVM Test
ViewModel ModelJVM Test
assert
when().thenReturn()
GDG DevFest – Milano – October 2015 – @fabioCollini 43
Mockito
@Test

public void testSaveData() {

//...


NoteModel model = viewModel.initAndResume();



model.getTitle().set("newTitle");

model.getText().set("newText");

viewModel.save();



verify(noteSaver)
.save(eq(123L), eq("newTitle"), eq("newText"));



verify(messageManager)
.showMessage(eq(R.string.note_saved));

}

GDG DevFest – Milano – October 2015 – @fabioCollini
MockMessage
Manager
Message
Manager
MockMessage
Manager
Message
Manager
44
ViewModel MockSaver
save
showMessage
Model
request
response
JVM Test
ViewModel MockSaverModelJVM Test
verify
verify
NoteSaver
NoteSaver
GDG DevFest – Milano – October 2015 – @fabioCollini 45
SetUp method
public class NoteViewModelTest {

private NoteLoader noteLoader;

private NoteSaver noteSaver;

private MessageManager messageManager;
private NoteViewModel viewModel;



@Before public void setUp() {

noteLoader = Mockito.mock(NoteLoader.class);

noteSaver = Mockito.mock(NoteSaver.class);

messageManager = Mockito.mock(MessageManager.class);


viewModel = new NoteViewModel(
noteLoader, noteSaver, messageManager);
when(noteLoader.load())

.thenReturn(new Note(123, "title", "text"));

}

//...

}
GDG DevFest – Milano – October 2015 – @fabioCollini 46
@Mock and @InjectMocks
@RunWith(MockitoJUnitRunner.class)

public class NoteViewModelTest {



@Mock NoteLoader noteLoader;



@Mock NoteSaver noteSaver;



@Mock MessageManager messageManager;



@InjectMocks NoteViewModel viewModel;



@Before public void setUp() throws Exception {

when(noteLoader.load())

.thenReturn(new Note(123, "title", "text"));

}


//...

}
GDG DevFest – Milano – October 2015 – @fabioCollini 47
Dagger
A fast dependency injector for Android and Java
v1 developed at Square
https://github.com/square/dagger
v2 developed at Google
https://github.com/google/dagger
Configuration using annotations and Java classes
Based on annotation processing (no reflection)
GDG DevFest – Milano – October 2015 – @fabioCollini 48
Background executor
if (titleValid && textValid) {

sending.set(true);

backgroundExecutor.execute(new Runnable() {

@Override public void run() {

try {

noteSaver.save(getModel().getNoteId(),
getModel().getTitle().get(),
getModel().getText().get());

hideSendProgressAndShowMessage(
R.string.note_saved);

} catch (RetrofitError e) {

hideSendProgressAndShowMessage(
R.string.error_saving_note);

}

}

});

}
GDG DevFest – Milano – October 2015 – @fabioCollini 49
Ui Executor
private void hideSendProgressAndShowMessage(final int msg) {

uiExecutor.execute(new Runnable() {

@Override public void run() {

messageManager.showMessage(msg);

sending.set(false);

}

});

}

GDG DevFest – Milano – October 2015 – @fabioCollini 50
Test using single thread
@RunWith(MockitoJUnitRunner.class)

public class NoteViewModelTest {



@Mock NoteView view;



@Mock NoteLoader noteLoader;



@Mock NoteSaver noteSaver;



@Spy Executor executor = new Executor() {

@Override public void execute(Runnable command) {

command.run();

}

};



@InjectMocks NoteViewModel viewModel;



//...

}
GDG DevFest - Milano - October 2015 - @fabioCollini 51
5Espresso
GDG DevFest – Milano – October 2015 – @fabioCollini 52
NoteLoader
public class NoteLoader {

private static NoteLoader instance;



public static NoteLoader singleton() {

if (instance == null) {

instance = new NoteLoader();

}

return instance;

}



private NoteLoader() {

}



@VisibleForTesting

public static void setInstance(NoteLoader instance) {

NoteLoader.instance = instance;

}



//...

}
GDG DevFest – Milano – October 2015 – @fabioCollini
public class NoteActivityTest {


@Rule public ActivityTestRule<NoteActivity> rule =
new ActivityTestRule<>(NoteActivity.class, false, false);



private NoteLoader noteLoader;


@Before public void setUp() throws Exception {

noteLoader = Mockito.mock(NoteLoader.class);

NoteLoader.setInstance(noteLoader);

}
//...

}
53
NoteActivityTest
GDG DevFest – Milano – October 2015 – @fabioCollini 54
Reload test
@Test

public void testReloadAfterError() {

when(noteLoader.load())

.thenThrow(
RetrofitError.networkError("url", new IOException()))

.thenReturn(new Note(123, "aaa", "bbb"));



rule.launchActivity(null);



onView(withText(R.string.retry)).perform(click());



onView(withText(“aaa"))
.check(matches(isDisplayed()));

onView(withText(“bbb"))
.check(matches(isDisplayed()));

}

GDG DevFest – Milano – October 2015 – @fabioCollini 55
View ViewModel MockLoader
perform(click())
update
binding
Model
request
response
EspressoTest
View ViewModel MockLoaderModelEspressoTest
onView
verify
NoteLoader
NoteLoader
when().thenReturn()
onClick
binding
GDG DevFest – Milano – October 2015 – @fabioCollini 56
Android Model View ViewModel
Activity (or Fragment) is the View
All the business logic is in the ViewModel
ViewModel is managed using Dependency Injection
Model is the Activity (or Fragment) state
ViewModel is retained on configuration change
ViewModel is testable using a JVM test
GDG DevFest – Milano – October 2015 – @fabioCollini 57
Links
mockito.org
joel-costigliola.github.io/assertj
Jay Fields - Working Effectively with Unit Tests
Michael Feathers - Working Effectively with Legacy Code
medium.com/@fabioCollini/android-data-binding-f9f9d3afc761
github.com/fabioCollini/mv2m
github.com/commit-non-javisti/CoseNonJavisteAndroidApp
GDG DevFest – Milano – October 2015 – @fabioCollini 58
Thanks for your attention!
androidavanzato.it
Questions?

More Related Content

What's hot

Kotlin Coroutines. Flow is coming
Kotlin Coroutines. Flow is comingKotlin Coroutines. Flow is coming
Kotlin Coroutines. Flow is comingKirill Rozov
 
Coroutines in Kotlin
Coroutines in KotlinCoroutines in Kotlin
Coroutines in KotlinAlexey Soshin
 
PUC SE Day 2019 - SpringBoot
PUC SE Day 2019 - SpringBootPUC SE Day 2019 - SpringBoot
PUC SE Day 2019 - SpringBootJosué Neis
 
200819 NAVER TECH CONCERT 03_화려한 코루틴이 내 앱을 감싸네! 코루틴으로 작성해보는 깔끔한 비동기 코드
200819 NAVER TECH CONCERT 03_화려한 코루틴이 내 앱을 감싸네! 코루틴으로 작성해보는 깔끔한 비동기 코드200819 NAVER TECH CONCERT 03_화려한 코루틴이 내 앱을 감싸네! 코루틴으로 작성해보는 깔끔한 비동기 코드
200819 NAVER TECH CONCERT 03_화려한 코루틴이 내 앱을 감싸네! 코루틴으로 작성해보는 깔끔한 비동기 코드NAVER Engineering
 
Threading Made Easy! A Busy Developer’s Guide to Kotlin Coroutines
Threading Made Easy! A Busy Developer’s Guide to Kotlin CoroutinesThreading Made Easy! A Busy Developer’s Guide to Kotlin Coroutines
Threading Made Easy! A Busy Developer’s Guide to Kotlin CoroutinesLauren Yew
 
Java Deserialization Vulnerabilities - The Forgotten Bug Class (DeepSec Edition)
Java Deserialization Vulnerabilities - The Forgotten Bug Class (DeepSec Edition)Java Deserialization Vulnerabilities - The Forgotten Bug Class (DeepSec Edition)
Java Deserialization Vulnerabilities - The Forgotten Bug Class (DeepSec Edition)CODE WHITE GmbH
 
Whitebox testing of Spring Boot applications
Whitebox testing of Spring Boot applicationsWhitebox testing of Spring Boot applications
Whitebox testing of Spring Boot applicationsYura Nosenko
 
Tester unitairement une application java
Tester unitairement une application javaTester unitairement une application java
Tester unitairement une application javaAntoine Rey
 
Inversion of Control and Dependency Injection
Inversion of Control and Dependency InjectionInversion of Control and Dependency Injection
Inversion of Control and Dependency InjectionDinesh Sharma
 
Java 8 - CJ
Java 8 - CJJava 8 - CJ
Java 8 - CJSunil OS
 
Hibernate
Hibernate Hibernate
Hibernate Sunil OS
 
java 8 new features
java 8 new features java 8 new features
java 8 new features Rohit Verma
 
Angular 2.0 forms
Angular 2.0 formsAngular 2.0 forms
Angular 2.0 formsEyal Vardi
 
Idiomatic Kotlin
Idiomatic KotlinIdiomatic Kotlin
Idiomatic Kotlinintelliyole
 
Analysing in depth work manager
Analysing in depth work managerAnalysing in depth work manager
Analysing in depth work managerbhatnagar.gaurav83
 
Javascript this keyword
Javascript this keywordJavascript this keyword
Javascript this keywordPham Huy Tung
 
L'API Collector dans tous ses états
L'API Collector dans tous ses étatsL'API Collector dans tous ses états
L'API Collector dans tous ses étatsJosé Paumard
 
Jsp/Servlet
Jsp/ServletJsp/Servlet
Jsp/ServletSunil OS
 

What's hot (20)

Kotlin Coroutines. Flow is coming
Kotlin Coroutines. Flow is comingKotlin Coroutines. Flow is coming
Kotlin Coroutines. Flow is coming
 
Coroutines in Kotlin
Coroutines in KotlinCoroutines in Kotlin
Coroutines in Kotlin
 
PUC SE Day 2019 - SpringBoot
PUC SE Day 2019 - SpringBootPUC SE Day 2019 - SpringBoot
PUC SE Day 2019 - SpringBoot
 
200819 NAVER TECH CONCERT 03_화려한 코루틴이 내 앱을 감싸네! 코루틴으로 작성해보는 깔끔한 비동기 코드
200819 NAVER TECH CONCERT 03_화려한 코루틴이 내 앱을 감싸네! 코루틴으로 작성해보는 깔끔한 비동기 코드200819 NAVER TECH CONCERT 03_화려한 코루틴이 내 앱을 감싸네! 코루틴으로 작성해보는 깔끔한 비동기 코드
200819 NAVER TECH CONCERT 03_화려한 코루틴이 내 앱을 감싸네! 코루틴으로 작성해보는 깔끔한 비동기 코드
 
Threading Made Easy! A Busy Developer’s Guide to Kotlin Coroutines
Threading Made Easy! A Busy Developer’s Guide to Kotlin CoroutinesThreading Made Easy! A Busy Developer’s Guide to Kotlin Coroutines
Threading Made Easy! A Busy Developer’s Guide to Kotlin Coroutines
 
Java Deserialization Vulnerabilities - The Forgotten Bug Class (DeepSec Edition)
Java Deserialization Vulnerabilities - The Forgotten Bug Class (DeepSec Edition)Java Deserialization Vulnerabilities - The Forgotten Bug Class (DeepSec Edition)
Java Deserialization Vulnerabilities - The Forgotten Bug Class (DeepSec Edition)
 
Flutter Intro
Flutter IntroFlutter Intro
Flutter Intro
 
Whitebox testing of Spring Boot applications
Whitebox testing of Spring Boot applicationsWhitebox testing of Spring Boot applications
Whitebox testing of Spring Boot applications
 
Tester unitairement une application java
Tester unitairement une application javaTester unitairement une application java
Tester unitairement une application java
 
Inversion of Control and Dependency Injection
Inversion of Control and Dependency InjectionInversion of Control and Dependency Injection
Inversion of Control and Dependency Injection
 
Java 8 - CJ
Java 8 - CJJava 8 - CJ
Java 8 - CJ
 
Hibernate
Hibernate Hibernate
Hibernate
 
java 8 new features
java 8 new features java 8 new features
java 8 new features
 
Angular 2.0 forms
Angular 2.0 formsAngular 2.0 forms
Angular 2.0 forms
 
Idiomatic Kotlin
Idiomatic KotlinIdiomatic Kotlin
Idiomatic Kotlin
 
JUnit 4
JUnit 4JUnit 4
JUnit 4
 
Analysing in depth work manager
Analysing in depth work managerAnalysing in depth work manager
Analysing in depth work manager
 
Javascript this keyword
Javascript this keywordJavascript this keyword
Javascript this keyword
 
L'API Collector dans tous ses états
L'API Collector dans tous ses étatsL'API Collector dans tous ses états
L'API Collector dans tous ses états
 
Jsp/Servlet
Jsp/ServletJsp/Servlet
Jsp/Servlet
 

Viewers also liked

Android Data Binding in action using MVVM pattern - droidconUK
Android Data Binding in action using MVVM pattern - droidconUKAndroid Data Binding in action using MVVM pattern - droidconUK
Android Data Binding in action using MVVM pattern - droidconUKFabio Collini
 
Testable Android Apps DroidCon Italy 2015
Testable Android Apps DroidCon Italy 2015Testable Android Apps DroidCon Italy 2015
Testable Android Apps DroidCon Italy 2015Fabio Collini
 
Android Wear CodeLab - GDG Firenze
Android Wear CodeLab - GDG FirenzeAndroid Wear CodeLab - GDG Firenze
Android Wear CodeLab - GDG FirenzeFabio Collini
 
Introduction to Retrofit and RxJava
Introduction to Retrofit and RxJavaIntroduction to Retrofit and RxJava
Introduction to Retrofit and RxJavaFabio Collini
 
Data Binding in Action using MVVM pattern
Data Binding in Action using MVVM patternData Binding in Action using MVVM pattern
Data Binding in Action using MVVM patternFabio Collini
 
MVVM with DataBinding on android
MVVM with DataBinding on androidMVVM with DataBinding on android
MVVM with DataBinding on androidRodrigo Bressan
 
Android Meetup Slovenija #3 - Testing with Robolectric by Ivan Kust
Android Meetup Slovenija #3 - Testing with Robolectric by Ivan KustAndroid Meetup Slovenija #3 - Testing with Robolectric by Ivan Kust
Android Meetup Slovenija #3 - Testing with Robolectric by Ivan KustInfinum
 
Moderne App-Architektur mit Dagger2 und RxJava
Moderne App-Architektur mit Dagger2 und RxJavaModerne App-Architektur mit Dagger2 und RxJava
Moderne App-Architektur mit Dagger2 und RxJavainovex GmbH
 
Android Widget @ whymca 2011
Android Widget @ whymca 2011Android Widget @ whymca 2011
Android Widget @ whymca 2011Fabio Collini
 
Clean android code - Droidcon Italiy 2014
Clean android code - Droidcon Italiy 2014Clean android code - Droidcon Italiy 2014
Clean android code - Droidcon Italiy 2014Fabio Collini
 
Android talks #08 dagger2
Android talks #08   dagger2Android talks #08   dagger2
Android talks #08 dagger2Infinum
 
Librerie su Android: come non reinventare la ruota @ whymca 2012
Librerie su Android: come non reinventare la ruota @ whymca 2012 Librerie su Android: come non reinventare la ruota @ whymca 2012
Librerie su Android: come non reinventare la ruota @ whymca 2012 Fabio Collini
 
Model-View-ViewModel and RxJava
Model-View-ViewModel and RxJavaModel-View-ViewModel and RxJava
Model-View-ViewModel and RxJavaFlorina Muntenescu
 
What's New in Visual Studio 2010
What's New in Visual Studio 2010What's New in Visual Studio 2010
What's New in Visual Studio 2010Adil Mughal
 
Windows 7 For Geeks
Windows 7 For GeeksWindows 7 For Geeks
Windows 7 For GeeksAdil Mughal
 
Community Contribution Experience
Community Contribution ExperienceCommunity Contribution Experience
Community Contribution ExperienceAdil Mughal
 
DevNext - Web Programming Concepts Using Asp Net
DevNext - Web Programming Concepts Using Asp NetDevNext - Web Programming Concepts Using Asp Net
DevNext - Web Programming Concepts Using Asp NetAdil Mughal
 
Dominando o Data Binding no Android
Dominando o Data Binding no AndroidDominando o Data Binding no Android
Dominando o Data Binding no AndroidNelson Glauber Leal
 

Viewers also liked (20)

Android Data Binding in action using MVVM pattern - droidconUK
Android Data Binding in action using MVVM pattern - droidconUKAndroid Data Binding in action using MVVM pattern - droidconUK
Android Data Binding in action using MVVM pattern - droidconUK
 
Testable Android Apps DroidCon Italy 2015
Testable Android Apps DroidCon Italy 2015Testable Android Apps DroidCon Italy 2015
Testable Android Apps DroidCon Italy 2015
 
Android Wear CodeLab - GDG Firenze
Android Wear CodeLab - GDG FirenzeAndroid Wear CodeLab - GDG Firenze
Android Wear CodeLab - GDG Firenze
 
Introduction to Retrofit and RxJava
Introduction to Retrofit and RxJavaIntroduction to Retrofit and RxJava
Introduction to Retrofit and RxJava
 
Data Binding in Action using MVVM pattern
Data Binding in Action using MVVM patternData Binding in Action using MVVM pattern
Data Binding in Action using MVVM pattern
 
MVVM with DataBinding on android
MVVM with DataBinding on androidMVVM with DataBinding on android
MVVM with DataBinding on android
 
Android Meetup Slovenija #3 - Testing with Robolectric by Ivan Kust
Android Meetup Slovenija #3 - Testing with Robolectric by Ivan KustAndroid Meetup Slovenija #3 - Testing with Robolectric by Ivan Kust
Android Meetup Slovenija #3 - Testing with Robolectric by Ivan Kust
 
Moderne App-Architektur mit Dagger2 und RxJava
Moderne App-Architektur mit Dagger2 und RxJavaModerne App-Architektur mit Dagger2 und RxJava
Moderne App-Architektur mit Dagger2 und RxJava
 
Android Widget @ whymca 2011
Android Widget @ whymca 2011Android Widget @ whymca 2011
Android Widget @ whymca 2011
 
Clean android code - Droidcon Italiy 2014
Clean android code - Droidcon Italiy 2014Clean android code - Droidcon Italiy 2014
Clean android code - Droidcon Italiy 2014
 
Android talks #08 dagger2
Android talks #08   dagger2Android talks #08   dagger2
Android talks #08 dagger2
 
Librerie su Android: come non reinventare la ruota @ whymca 2012
Librerie su Android: come non reinventare la ruota @ whymca 2012 Librerie su Android: come non reinventare la ruota @ whymca 2012
Librerie su Android: come non reinventare la ruota @ whymca 2012
 
Model-View-ViewModel and RxJava
Model-View-ViewModel and RxJavaModel-View-ViewModel and RxJava
Model-View-ViewModel and RxJava
 
Data binding
Data bindingData binding
Data binding
 
What's New in Visual Studio 2010
What's New in Visual Studio 2010What's New in Visual Studio 2010
What's New in Visual Studio 2010
 
Windows 7 For Geeks
Windows 7 For GeeksWindows 7 For Geeks
Windows 7 For Geeks
 
Community Contribution Experience
Community Contribution ExperienceCommunity Contribution Experience
Community Contribution Experience
 
DevNext - Web Programming Concepts Using Asp Net
DevNext - Web Programming Concepts Using Asp NetDevNext - Web Programming Concepts Using Asp Net
DevNext - Web Programming Concepts Using Asp Net
 
Dominando o Data Binding no Android
Dominando o Data Binding no AndroidDominando o Data Binding no Android
Dominando o Data Binding no Android
 
Android Databinding Library
Android Databinding LibraryAndroid Databinding Library
Android Databinding Library
 

Similar to Testable Android Apps using data binding and MVVM

Встреча Google Post IO ( Владимир Иванов, Катерина Заворотченко и Сергей Комлач)
Встреча Google Post IO ( Владимир Иванов, Катерина Заворотченко и Сергей Комлач)Встреча Google Post IO ( Владимир Иванов, Катерина Заворотченко и Сергей Комлач)
Встреча Google Post IO ( Владимир Иванов, Катерина Заворотченко и Сергей Комлач)Alina Vilk
 
Avoiding and dealing with conflicting updates in Oak
Avoiding and dealing with conflicting updates in OakAvoiding and dealing with conflicting updates in Oak
Avoiding and dealing with conflicting updates in Oakmichid
 
Google Plus SignIn : l'Authentification Google
Google Plus SignIn : l'Authentification GoogleGoogle Plus SignIn : l'Authentification Google
Google Plus SignIn : l'Authentification GoogleMathias Seguy
 
Guice tutorial
Guice tutorialGuice tutorial
Guice tutorialAnh Quân
 
How to code to code less
How to code to code lessHow to code to code less
How to code to code lessAnton Novikau
 
Android architecture
Android architecture Android architecture
Android architecture Trong-An Bui
 
My way to clean android v2 English DroidCon Spain
My way to clean android v2 English DroidCon SpainMy way to clean android v2 English DroidCon Spain
My way to clean android v2 English DroidCon SpainChristian Panadero
 
Loopback: An Easy and Robust Mobile Backend - Michael Hantler & Aviv Callande...
Loopback: An Easy and Robust Mobile Backend - Michael Hantler & Aviv Callande...Loopback: An Easy and Robust Mobile Backend - Michael Hantler & Aviv Callande...
Loopback: An Easy and Robust Mobile Backend - Michael Hantler & Aviv Callande...Codemotion Tel Aviv
 
Sharper Better Faster Dagger ‡ - Droidcon SF
Sharper Better Faster Dagger ‡ - Droidcon SFSharper Better Faster Dagger ‡ - Droidcon SF
Sharper Better Faster Dagger ‡ - Droidcon SFPierre-Yves Ricau
 
ColdBox APIs + VueJS - powering Mobile, Desktop and Web Apps with 1 VueJS cod...
ColdBox APIs + VueJS - powering Mobile, Desktop and Web Apps with 1 VueJS cod...ColdBox APIs + VueJS - powering Mobile, Desktop and Web Apps with 1 VueJS cod...
ColdBox APIs + VueJS - powering Mobile, Desktop and Web Apps with 1 VueJS cod...Gavin Pickin
 
Comment développer une application mobile en 8 semaines - Meetup PAUG 24-01-2023
Comment développer une application mobile en 8 semaines - Meetup PAUG 24-01-2023Comment développer une application mobile en 8 semaines - Meetup PAUG 24-01-2023
Comment développer une application mobile en 8 semaines - Meetup PAUG 24-01-2023Nicolas HAAN
 
Advanced Dagger talk from 360andev
Advanced Dagger talk from 360andevAdvanced Dagger talk from 360andev
Advanced Dagger talk from 360andevMike Nakhimovich
 
GitOps: Git come unica fonte di verità per applicazioni e infrastruttura
GitOps: Git come unica fonte di verità per applicazioni e infrastrutturaGitOps: Git come unica fonte di verità per applicazioni e infrastruttura
GitOps: Git come unica fonte di verità per applicazioni e infrastrutturasparkfabrik
 
Predictable Web Apps with Angular and Redux
Predictable Web Apps with Angular and ReduxPredictable Web Apps with Angular and Redux
Predictable Web Apps with Angular and ReduxFITC
 
Using Dagger in a Clean Architecture project
Using Dagger in a Clean Architecture projectUsing Dagger in a Clean Architecture project
Using Dagger in a Clean Architecture projectFabio Collini
 
Bridging the Gap: Single-Page Apps and AEM
Bridging the Gap: Single-Page Apps and AEMBridging the Gap: Single-Page Apps and AEM
Bridging the Gap: Single-Page Apps and AEMrbl002
 

Similar to Testable Android Apps using data binding and MVVM (20)

Встреча Google Post IO ( Владимир Иванов, Катерина Заворотченко и Сергей Комлач)
Встреча Google Post IO ( Владимир Иванов, Катерина Заворотченко и Сергей Комлач)Встреча Google Post IO ( Владимир Иванов, Катерина Заворотченко и Сергей Комлач)
Встреча Google Post IO ( Владимир Иванов, Катерина Заворотченко и Сергей Комлач)
 
Avoiding and dealing with conflicting updates in Oak
Avoiding and dealing with conflicting updates in OakAvoiding and dealing with conflicting updates in Oak
Avoiding and dealing with conflicting updates in Oak
 
Google Plus SignIn : l'Authentification Google
Google Plus SignIn : l'Authentification GoogleGoogle Plus SignIn : l'Authentification Google
Google Plus SignIn : l'Authentification Google
 
Google GIN
Google GINGoogle GIN
Google GIN
 
Guice tutorial
Guice tutorialGuice tutorial
Guice tutorial
 
Dagger for dummies
Dagger for dummiesDagger for dummies
Dagger for dummies
 
How to code to code less
How to code to code lessHow to code to code less
How to code to code less
 
Android architecture
Android architecture Android architecture
Android architecture
 
Griffon Presentation
Griffon PresentationGriffon Presentation
Griffon Presentation
 
My way to clean android v2 English DroidCon Spain
My way to clean android v2 English DroidCon SpainMy way to clean android v2 English DroidCon Spain
My way to clean android v2 English DroidCon Spain
 
Loopback: An Easy and Robust Mobile Backend - Michael Hantler & Aviv Callande...
Loopback: An Easy and Robust Mobile Backend - Michael Hantler & Aviv Callande...Loopback: An Easy and Robust Mobile Backend - Michael Hantler & Aviv Callande...
Loopback: An Easy and Robust Mobile Backend - Michael Hantler & Aviv Callande...
 
Sharper Better Faster Dagger ‡ - Droidcon SF
Sharper Better Faster Dagger ‡ - Droidcon SFSharper Better Faster Dagger ‡ - Droidcon SF
Sharper Better Faster Dagger ‡ - Droidcon SF
 
ColdBox APIs + VueJS - powering Mobile, Desktop and Web Apps with 1 VueJS cod...
ColdBox APIs + VueJS - powering Mobile, Desktop and Web Apps with 1 VueJS cod...ColdBox APIs + VueJS - powering Mobile, Desktop and Web Apps with 1 VueJS cod...
ColdBox APIs + VueJS - powering Mobile, Desktop and Web Apps with 1 VueJS cod...
 
Modern android development
Modern android developmentModern android development
Modern android development
 
Comment développer une application mobile en 8 semaines - Meetup PAUG 24-01-2023
Comment développer une application mobile en 8 semaines - Meetup PAUG 24-01-2023Comment développer une application mobile en 8 semaines - Meetup PAUG 24-01-2023
Comment développer une application mobile en 8 semaines - Meetup PAUG 24-01-2023
 
Advanced Dagger talk from 360andev
Advanced Dagger talk from 360andevAdvanced Dagger talk from 360andev
Advanced Dagger talk from 360andev
 
GitOps: Git come unica fonte di verità per applicazioni e infrastruttura
GitOps: Git come unica fonte di verità per applicazioni e infrastrutturaGitOps: Git come unica fonte di verità per applicazioni e infrastruttura
GitOps: Git come unica fonte di verità per applicazioni e infrastruttura
 
Predictable Web Apps with Angular and Redux
Predictable Web Apps with Angular and ReduxPredictable Web Apps with Angular and Redux
Predictable Web Apps with Angular and Redux
 
Using Dagger in a Clean Architecture project
Using Dagger in a Clean Architecture projectUsing Dagger in a Clean Architecture project
Using Dagger in a Clean Architecture project
 
Bridging the Gap: Single-Page Apps and AEM
Bridging the Gap: Single-Page Apps and AEMBridging the Gap: Single-Page Apps and AEM
Bridging the Gap: Single-Page Apps and AEM
 

More from Fabio Collini

Architectures in the compose world
Architectures in the compose worldArchitectures in the compose world
Architectures in the compose worldFabio Collini
 
Managing parallelism using coroutines
Managing parallelism using coroutinesManaging parallelism using coroutines
Managing parallelism using coroutinesFabio Collini
 
Kotlin Delegates in practice - Kotlin community conf
Kotlin Delegates in practice - Kotlin community confKotlin Delegates in practice - Kotlin community conf
Kotlin Delegates in practice - Kotlin community confFabio Collini
 
Kotlin delegates in practice - Kotlin Everywhere Stockholm
Kotlin delegates in practice - Kotlin Everywhere StockholmKotlin delegates in practice - Kotlin Everywhere Stockholm
Kotlin delegates in practice - Kotlin Everywhere StockholmFabio Collini
 
Solid principles in practice the clean architecture - Droidcon Italy
Solid principles in practice the clean architecture - Droidcon ItalySolid principles in practice the clean architecture - Droidcon Italy
Solid principles in practice the clean architecture - Droidcon ItalyFabio Collini
 
SOLID principles in practice: the Clean Architecture - Devfest Emila Romagna
SOLID principles in practice: the Clean Architecture - Devfest Emila RomagnaSOLID principles in practice: the Clean Architecture - Devfest Emila Romagna
SOLID principles in practice: the Clean Architecture - Devfest Emila RomagnaFabio Collini
 
SOLID principles in practice: the Clean Architecture
SOLID principles in practice: the Clean ArchitectureSOLID principles in practice: the Clean Architecture
SOLID principles in practice: the Clean ArchitectureFabio Collini
 
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf Milan
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf MilanFrom Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf Milan
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf MilanFabio Collini
 
Async code on kotlin: rx java or/and coroutines - Kotlin Night Turin
Async code on kotlin: rx java or/and coroutines - Kotlin Night TurinAsync code on kotlin: rx java or/and coroutines - Kotlin Night Turin
Async code on kotlin: rx java or/and coroutines - Kotlin Night TurinFabio Collini
 
Recap Google I/O 2018
Recap Google I/O 2018Recap Google I/O 2018
Recap Google I/O 2018Fabio Collini
 
From java to kotlin beyond alt+shift+cmd+k - Droidcon italy
From java to kotlin beyond alt+shift+cmd+k - Droidcon italyFrom java to kotlin beyond alt+shift+cmd+k - Droidcon italy
From java to kotlin beyond alt+shift+cmd+k - Droidcon italyFabio Collini
 
From java to kotlin beyond alt+shift+cmd+k
From java to kotlin beyond alt+shift+cmd+kFrom java to kotlin beyond alt+shift+cmd+k
From java to kotlin beyond alt+shift+cmd+kFabio Collini
 
Testing Android apps based on Dagger and RxJava Droidcon UK
Testing Android apps based on Dagger and RxJava Droidcon UKTesting Android apps based on Dagger and RxJava Droidcon UK
Testing Android apps based on Dagger and RxJava Droidcon UKFabio Collini
 
Intro to Retrofit 2 and RxJava2
Intro to Retrofit 2 and RxJava2Intro to Retrofit 2 and RxJava2
Intro to Retrofit 2 and RxJava2Fabio Collini
 
Testing Android apps based on Dagger and RxJava
Testing Android apps based on Dagger and RxJavaTesting Android apps based on Dagger and RxJava
Testing Android apps based on Dagger and RxJavaFabio Collini
 

More from Fabio Collini (15)

Architectures in the compose world
Architectures in the compose worldArchitectures in the compose world
Architectures in the compose world
 
Managing parallelism using coroutines
Managing parallelism using coroutinesManaging parallelism using coroutines
Managing parallelism using coroutines
 
Kotlin Delegates in practice - Kotlin community conf
Kotlin Delegates in practice - Kotlin community confKotlin Delegates in practice - Kotlin community conf
Kotlin Delegates in practice - Kotlin community conf
 
Kotlin delegates in practice - Kotlin Everywhere Stockholm
Kotlin delegates in practice - Kotlin Everywhere StockholmKotlin delegates in practice - Kotlin Everywhere Stockholm
Kotlin delegates in practice - Kotlin Everywhere Stockholm
 
Solid principles in practice the clean architecture - Droidcon Italy
Solid principles in practice the clean architecture - Droidcon ItalySolid principles in practice the clean architecture - Droidcon Italy
Solid principles in practice the clean architecture - Droidcon Italy
 
SOLID principles in practice: the Clean Architecture - Devfest Emila Romagna
SOLID principles in practice: the Clean Architecture - Devfest Emila RomagnaSOLID principles in practice: the Clean Architecture - Devfest Emila Romagna
SOLID principles in practice: the Clean Architecture - Devfest Emila Romagna
 
SOLID principles in practice: the Clean Architecture
SOLID principles in practice: the Clean ArchitectureSOLID principles in practice: the Clean Architecture
SOLID principles in practice: the Clean Architecture
 
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf Milan
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf MilanFrom Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf Milan
From Java to Kotlin beyond alt+shift+cmd+k - Kotlin Community Conf Milan
 
Async code on kotlin: rx java or/and coroutines - Kotlin Night Turin
Async code on kotlin: rx java or/and coroutines - Kotlin Night TurinAsync code on kotlin: rx java or/and coroutines - Kotlin Night Turin
Async code on kotlin: rx java or/and coroutines - Kotlin Night Turin
 
Recap Google I/O 2018
Recap Google I/O 2018Recap Google I/O 2018
Recap Google I/O 2018
 
From java to kotlin beyond alt+shift+cmd+k - Droidcon italy
From java to kotlin beyond alt+shift+cmd+k - Droidcon italyFrom java to kotlin beyond alt+shift+cmd+k - Droidcon italy
From java to kotlin beyond alt+shift+cmd+k - Droidcon italy
 
From java to kotlin beyond alt+shift+cmd+k
From java to kotlin beyond alt+shift+cmd+kFrom java to kotlin beyond alt+shift+cmd+k
From java to kotlin beyond alt+shift+cmd+k
 
Testing Android apps based on Dagger and RxJava Droidcon UK
Testing Android apps based on Dagger and RxJava Droidcon UKTesting Android apps based on Dagger and RxJava Droidcon UK
Testing Android apps based on Dagger and RxJava Droidcon UK
 
Intro to Retrofit 2 and RxJava2
Intro to Retrofit 2 and RxJava2Intro to Retrofit 2 and RxJava2
Intro to Retrofit 2 and RxJava2
 
Testing Android apps based on Dagger and RxJava
Testing Android apps based on Dagger and RxJavaTesting Android apps based on Dagger and RxJava
Testing Android apps based on Dagger and RxJava
 

Recently uploaded

Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)OPEN KNOWLEDGE GmbH
 
chapter--4-software-project-planning.ppt
chapter--4-software-project-planning.pptchapter--4-software-project-planning.ppt
chapter--4-software-project-planning.pptkotipi9215
 
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...MyIntelliSource, Inc.
 
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdfGOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdfAlina Yurenko
 
Salesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantSalesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantAxelRicardoTrocheRiq
 
Unveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsUnveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsAhmed Mohamed
 
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...Christina Lin
 
React Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaReact Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaHanief Utama
 
Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...OnePlan Solutions
 
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer DataAdobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer DataBradBedford3
 
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...gurkirankumar98700
 
Cloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEECloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEEVICTOR MAESTRE RAMIREZ
 
Building Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop SlideBuilding Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop SlideChristina Lin
 
Folding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesFolding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesPhilip Schwarz
 
Professional Resume Template for Software Developers
Professional Resume Template for Software DevelopersProfessional Resume Template for Software Developers
Professional Resume Template for Software DevelopersVinodh Ram
 
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptxKnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptxTier1 app
 
Asset Management Software - Infographic
Asset Management Software - InfographicAsset Management Software - Infographic
Asset Management Software - InfographicHr365.us smith
 
What is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWhat is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWave PLM
 

Recently uploaded (20)

Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)Der Spagat zwischen BIAS und FAIRNESS (2024)
Der Spagat zwischen BIAS und FAIRNESS (2024)
 
chapter--4-software-project-planning.ppt
chapter--4-software-project-planning.pptchapter--4-software-project-planning.ppt
chapter--4-software-project-planning.ppt
 
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
Try MyIntelliAccount Cloud Accounting Software As A Service Solution Risk Fre...
 
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdfGOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
 
Salesforce Certified Field Service Consultant
Salesforce Certified Field Service ConsultantSalesforce Certified Field Service Consultant
Salesforce Certified Field Service Consultant
 
Unveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsUnveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML Diagrams
 
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
 
React Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaReact Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief Utama
 
Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...
 
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer DataAdobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
 
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
(Genuine) Escort Service Lucknow | Starting ₹,5K To @25k with A/C 🧑🏽‍❤️‍🧑🏻 89...
 
Cloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEECloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEE
 
Call Girls In Mukherjee Nagar 📱 9999965857 🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SE...
Call Girls In Mukherjee Nagar 📱  9999965857  🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SE...Call Girls In Mukherjee Nagar 📱  9999965857  🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SE...
Call Girls In Mukherjee Nagar 📱 9999965857 🤩 Delhi 🫦 HOT AND SEXY VVIP 🍎 SE...
 
Building Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop SlideBuilding Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
Building Real-Time Data Pipelines: Stream & Batch Processing workshop Slide
 
Folding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a seriesFolding Cheat Sheet #4 - fourth in a series
Folding Cheat Sheet #4 - fourth in a series
 
Professional Resume Template for Software Developers
Professional Resume Template for Software DevelopersProfessional Resume Template for Software Developers
Professional Resume Template for Software Developers
 
Hot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort Service
Hot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort ServiceHot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort Service
Hot Sexy call girls in Patel Nagar🔝 9953056974 🔝 escort Service
 
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptxKnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
KnowAPIs-UnknownPerf-jaxMainz-2024 (1).pptx
 
Asset Management Software - Infographic
Asset Management Software - InfographicAsset Management Software - Infographic
Asset Management Software - Infographic
 
What is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWhat is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need It
 

Testable Android Apps using data binding and MVVM

  • 1. Testable Android Apps using data binding and MVVM Fabio Collini
  • 2. GDG DevFest – Milano – October 2015 – @fabioCollini 2 Ego slide @fabioCollini linkedin.com/in/fabiocollini Folder Organizer cosenonjaviste.it nana bianca Freapp instal.com Rain tomorrow?
  • 3. GDG DevFest – Milano – October 2015 – @fabioCollini 3 Agenda 1. ROI and Legacy code 2. Model View ViewModel 3. JVM Unit tests 4. Mockito 5. Espresso
  • 4. GDG DevFest - Milano - October 2015 - @fabioCollini 4 1ROI and legacy code
  • 5. GDG DevFest – Milano – October 2015 – @fabioCollini 5 Quick survey Do you write automated tests?
  • 6. GDG DevFest – Milano – October 2015 – @fabioCollini 6
  • 7. GDG DevFest – Milano – October 2015 – @fabioCollini 7 Return of Investment - ROI Net profit Investment
  • 8. GDG DevFest – Milano – October 2015 – @fabioCollini 8 Legacy code Edit and pray Vs Cover and modify Legacy code is code without unit tests
  • 9. GDG DevFest – Milano – October 2015 – @fabioCollini 9 Test After Development Write the feature implementation Do some manual testing Try to write automatic tests Modify the initial implementation to test it “Standard” Android code is not testable :(
  • 10. GDG DevFest – Milano – October 2015 – @fabioCollini 10 Legacy code dilemma When we change code, we should have tests in place. To put tests in place, we often have to change code. Michael Feathers
  • 11. GDG DevFest - Milano - October 2015 - @fabioCollini 11 2Model View ViewModel
  • 12. GDG DevFest – Milano – October 2015 – @fabioCollini 12 Testable code Data binding and MVVM
  • 13. GDG DevFest – Milano – October 2015 – @fabioCollini 13 Model View ViewModel View ViewModel Model DataBinding
  • 14. GDG DevFest – Milano – October 2015 – @fabioCollini 14 Android Model View ViewModel View ViewModel Model DataBinding Retained on configuration change Saved in Activity or Fragment state Activity or Fragment
  • 15. GDG DevFest – Milano – October 2015 – @fabioCollini 15 mv2m https://github.com/fabioCollini/mv2m
  • 16. GDG DevFest – Milano – October 2015 – @fabioCollini 16 NoteActivity NoteViewModel NoteModel note_detail.xml NoteDetailBinding DataBinding
  • 17. GDG DevFest – Milano – October 2015 – @fabioCollini 17 View ViewModel RetrofitService onClick update binding Model View ViewModel RetrofitServiceModel request response binding
  • 18. GDG DevFest – Milano – October 2015 – @fabioCollini 18 NoteModel Saved on Activity state public class NoteModel implements Parcelable {
 
 private long noteId;
 
 private ObservableBoolean error = new ObservableBoolean();
 
 private ObservableString title = new ObservableString();
 
 private ObservableString text = new ObservableString();
 
 private ObservableInt titleError = new ObservableInt();
 
 private ObservableInt textError = new ObservableInt();
 
 //...
 }
  • 19. GDG DevFest – Milano – October 2015 – @fabioCollini 19 NoteActivity public class NoteActivity extends ViewModelActivity<NoteViewModel> {
 
 @Override public NoteViewModel createViewModel() {
 return new NoteViewModel(/* .. */);
 }
 
 @Override protected void onCreate(Bundle state) {
 super.onCreate(state);
 NoteDetailBinding binding = DataBindingUtil.setContentView(this, R.layout.note_detail);
 binding.setViewModel(viewModel);
 }
 }
  • 20. GDG DevFest – Milano – October 2015 – @fabioCollini 20 note_detail.xml <?xml version="1.0" encoding="utf-8"?>
 <layout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:app="http://schemas.android.com/apk/res-auto">
 
 <data>
 <variable
 name="viewModel"
 type="it.cosenonjaviste.core.NoteViewModel"/>
 </data>
 
 <FrameLayout
 android:layout_width="match_parent"
 android:layout_height="match_parent">
 
 <!-- ... -->
 
 </FrameLayout>
 </layout>
  • 21. GDG DevFest – Milano – October 2015 – @fabioCollini 21 note_detail.xml <LinearLayout app:visible="@{viewModel.loading}">
 <ProgressBar />
 <TextView />
 </LinearLayout>
 
 <LinearLayout app:visible=“@{viewModel.model.error}"> 
 <TextView android:text="@string/error_loading_note"/>
 
 <Button android:text=“@string/retry"/>
 </LinearLayout>
 
 <ScrollView app:visible="@{!viewModel.loading &amp;&amp; !viewModel.model.error}">
 <!-- ... -->
 </ScrollView>
  • 22. GDG DevFest – Milano – October 2015 – @fabioCollini 22 note_detail.xml <LinearLayout>
 <android.support.design.widget.TextInputLayout
 app:error=“@{viewModel.model.titleError}"> 
 <EditText app:binding=“@{viewModel.model.title}" /> 
 </android.support.design.widget.TextInputLayout>
 <!-- ... --> <RelativeLayout> 
 <Button android:enabled="@{!viewModel.sending}"
 app:onClick="@{viewModel.save}"/>
 <ProgressBar app:visible=“@{viewModel.sending}" />
 </RelativeLayout>
 </LinearLayout>
  • 23. GDG DevFest – Milano – October 2015 – @fabioCollini 23 app:binding @BindingAdapter({"app:binding"})
 public static void bindEditText(EditText view, final ObservableString observableString) {
 if (view.getTag(R.id.binded) == null) {
 view.setTag(R.id.binded, true);
 view.addTextChangedListener(new TextWatcherAdapter() {
 @Override public void onTextChanged( CharSequence s, int st, int b, int c) {
 observableString.set(s.toString());
 }
 });
 }
 String newValue = observableString.get();
 if (!view.getText().toString().equals(newValue)) {
 view.setText(newValue);
 }
 }
  • 24. GDG DevFest – Milano – October 2015 – @fabioCollini 24 app:visible app:onClick @BindingAdapter({"app:visible"})
 public static void bindVisible(View view, boolean b) {
 view.setVisibility(b ? View.VISIBLE : View.INVISIBLE);
 }
 
 @BindingAdapter({"app:onClick"})
 public static void bindOnClick(View view, final Runnable listener) {
 view.setOnClickListener(new View.OnClickListener() {
 @Override public void onClick(View v) {
 listener.run();
 }
 });
 }
  • 25. GDG DevFest – Milano – October 2015 – @fabioCollini 25 NoteViewModel public class NoteViewModel extends ViewModel<NoteModel> {
 
 //...
 
 @Override public NoteModel createDefaultModel() {
 return new NoteModel();
 }
 
 @Override public void resume() {
 if (!getModel().isLoaded()) {
 reloadData();
 }
 }
 
 public void reloadData() {
 }
 
 //...
 }
  • 26. GDG DevFest - Milano - October 2015 - @fabioCollini 26 3JVM Unit tests
  • 27. GDG DevFest – Milano – October 2015 – @fabioCollini 27 Instrumentation tests run on a device (real or emulated) high code coverage Vs JVM tests fast low code coverage
  • 28. GDG DevFest – Milano – October 2015 – @fabioCollini JVM Test 28 NoteActivity NoteViewModel NoteModel note_detail.xml NoteDetailBinding DataBinding
  • 29. GDG DevFest – Milano – October 2015 – @fabioCollini 29 NoteViewModel.reloadData public class NoteViewModel extends ViewModel<NoteModel> {
 
 //...
 @Override public void resume() {
 if (!getModel().isLoaded()) {
 reloadData();
 }
 }
 
 public void reloadData() {
 try {
 Note note = NoteLoader.singleton().load();
 getModel().update(note);
 } catch (Exception e) {
 getModel().getError().set(true);
 }
 }
 //...
 }
  • 30. GDG DevFest – Milano – October 2015 – @fabioCollini 30 First test AssertJ @Test
 public void testLoadData() {
 NoteViewModel viewModel = new NoteViewModel();
 
 NoteModel model = viewModel.initAndResume();
 
 assertThat(model.getTitle().get()).isEqualTo("???");
 assertThat(model.getText().get()).isEqualTo("???");
 assertThat(model.getError().get()).isFalse();
 }
  • 31. GDG DevFest – Milano – October 2015 – @fabioCollini 31 NoteLoader.singleton public class NoteViewModel extends ViewModel<NoteModel> {
 
 //...
 @Override public void resume() {
 if (!getModel().isLoaded()) {
 reloadData();
 }
 }
 
 public void reloadData() {
 try {
 Note note = NoteLoader.singleton().load();
 getModel().update(note);
 } catch (Exception e) {
 getModel().getError().set(true);
 }
 }
 //...
 }
  • 32. GDG DevFest – Milano – October 2015 – @fabioCollini 32 Dependency Injection public class NoteViewModel extends ViewModel<NoteModel, NoteView> { private NoteLoader noteLoader;
 public NoteViewModel(NoteLoader noteLoader) {
 this.noteLoader = noteLoader;
 } 
 public void reloadData() {
 try {
 Note note = noteLoader.load();
 getModel().update(note);
 } catch (Exception e) {
 getModel().getError().set(true);
 }
 } //... }
  • 33. GDG DevFest – Milano – October 2015 – @fabioCollini 33 NoteLoaderStub public class NoteLoaderStub implements NoteLoader { 
 private Note note;
 
 public NoteLoaderStub(Note note) {
 this.note = note;
 }
 
 @Override public Note load() {
 return note;
 }
 }
  • 34. GDG DevFest – Milano – October 2015 – @fabioCollini 34 Test with stub @Test
 public void testLoadData() {
 NoteLoaderStub stub = new NoteLoaderStub(new Note(1, "a", "b")); 
 NoteViewModel viewModel = new NoteViewModel(stub);
 
 NoteModel model = viewModel.initAndResume();
 
 assertThat(model.getTitle().get()).isEqualTo("a");
 assertThat(model.getText().get()).isEqualTo("b");
 assertThat(model.getError().get()).isFalse();
 }
  • 35. GDG DevFest – Milano – October 2015 – @fabioCollini public void save() {
 NoteModel model = getModel();
 boolean titleValid = checkMandatory( model.getTitle(), model.getTitleError());
 boolean textValid = checkMandatory( model.getText(), model.getTextError());
 if (titleValid && textValid) {
 try {
 noteSaver.save( model.getNoteId(), model.getTitle().get(), model.getText().get()); messageManager.showMessage(R.string.note_saved);
 } catch (RetrofitError e) {
 messageManager.showMessage( R.string.error_saving_note);
 }
 }
 } 35 save method Dependency Injection Dependency Injection
  • 36. GDG DevFest – Milano – October 2015 – @fabioCollini 36 SnackbarMessageManager public class SnackbarMessageManager implements MessageManager {
 private Activity activity;
 
 @Override public void showMessage(int message) {
 if (activity != null) {
 Snackbar.make( activity.findViewById(android.R.id.content), message, Snackbar.LENGTH_LONG ).show();
 }
 }
 
 @Override public void setActivity(Activity activity) {
 this.activity = activity;
 }
 }
  • 37. GDG DevFest – Milano – October 2015 – @fabioCollini 37 MessageManagerSpy public class MessageManagerSpy implements MessageManager {
 public int message;
 
 @Override public void showMessage(int message) {
 this.message = message;
 }
 
 @Override public void setActivity(Activity activity) {
 }
 }
  • 38. GDG DevFest – Milano – October 2015 – @fabioCollini 38 NoteSaverSpy public class NoteSaverSpy implements NoteSaver {
 
 public long id;
 public String title;
 public String text;
 
 @Override public Response save( long id, String title, String text) {
 this.id = id;
 this.title = title;
 this.text = text;
 return null;
 }
 }
  • 39. GDG DevFest – Milano – October 2015 – @fabioCollini 39 Test with spy @Test
 public void testSaveData() {
 NoteLoaderStub stub = new NoteLoaderStub(new Note(1, "a", "b")); NoteSaverSpy saverSpy = new NoteSaverSpy();
 MessageManagerSpy messageSpy = new MessageManagerSpy(); NoteViewModel viewModel = new NoteViewModel( stub, saverSpy, messageSpy); 
 NoteModel model = viewModel.initAndResume();
 model.getTitle().set("newTitle");
 model.getText().set("newText");
 viewModel.save();
 
 assertThat(saverSpy.id).isEqualTo(1L);
 assertThat(saverSpy.title).isEqualTo("newTitle");
 assertThat(saverSpy.text).isEqualTo("newText"); assertThat(messageSpy.message) .isEqualTo(R.string.note_saved);
 }
  • 40. GDG DevFest - Milano - October 2015 - @fabioCollini 40 4Mockito
  • 41. GDG DevFest – Milano – October 2015 – @fabioCollini 41 Mockito @Test
 public void testLoadData() {
 NoteLoader noteLoader =
 Mockito.mock(NoteLoader.class);
 NoteSaver noteSaver = 
 Mockito.mock(NoteSaver.class); MessageManager messageManager = Mockito.mock(MessageManager.class); 
 NoteViewModel viewModel = new NoteViewModel(
 noteLoader, noteSaver, messageManager);
 
 when(noteLoader.load())
 .thenReturn(new Note(123, "title", "text"));
 NoteModel model = viewModel.initAndResume();
 
 assertThat(model.getTitle().get()).isEqualTo("title");
 assertThat(model.getText().get()).isEqualTo("text");
 }
  • 42. GDG DevFest – Milano – October 2015 – @fabioCollini MockLoader MockLoaderNoteLoader NoteLoader 42 ViewModel initAndResume update Model request response JVM Test ViewModel ModelJVM Test assert when().thenReturn()
  • 43. GDG DevFest – Milano – October 2015 – @fabioCollini 43 Mockito @Test
 public void testSaveData() {
 //... 
 NoteModel model = viewModel.initAndResume();
 
 model.getTitle().set("newTitle");
 model.getText().set("newText");
 viewModel.save();
 
 verify(noteSaver) .save(eq(123L), eq("newTitle"), eq("newText"));
 
 verify(messageManager) .showMessage(eq(R.string.note_saved));
 }

  • 44. GDG DevFest – Milano – October 2015 – @fabioCollini MockMessage Manager Message Manager MockMessage Manager Message Manager 44 ViewModel MockSaver save showMessage Model request response JVM Test ViewModel MockSaverModelJVM Test verify verify NoteSaver NoteSaver
  • 45. GDG DevFest – Milano – October 2015 – @fabioCollini 45 SetUp method public class NoteViewModelTest {
 private NoteLoader noteLoader;
 private NoteSaver noteSaver;
 private MessageManager messageManager; private NoteViewModel viewModel;
 
 @Before public void setUp() {
 noteLoader = Mockito.mock(NoteLoader.class);
 noteSaver = Mockito.mock(NoteSaver.class);
 messageManager = Mockito.mock(MessageManager.class); 
 viewModel = new NoteViewModel( noteLoader, noteSaver, messageManager); when(noteLoader.load())
 .thenReturn(new Note(123, "title", "text"));
 }
 //...
 }
  • 46. GDG DevFest – Milano – October 2015 – @fabioCollini 46 @Mock and @InjectMocks @RunWith(MockitoJUnitRunner.class)
 public class NoteViewModelTest {
 
 @Mock NoteLoader noteLoader;
 
 @Mock NoteSaver noteSaver;
 
 @Mock MessageManager messageManager;
 
 @InjectMocks NoteViewModel viewModel;
 
 @Before public void setUp() throws Exception {
 when(noteLoader.load())
 .thenReturn(new Note(123, "title", "text"));
 } 
 //...
 }
  • 47. GDG DevFest – Milano – October 2015 – @fabioCollini 47 Dagger A fast dependency injector for Android and Java v1 developed at Square https://github.com/square/dagger v2 developed at Google https://github.com/google/dagger Configuration using annotations and Java classes Based on annotation processing (no reflection)
  • 48. GDG DevFest – Milano – October 2015 – @fabioCollini 48 Background executor if (titleValid && textValid) {
 sending.set(true);
 backgroundExecutor.execute(new Runnable() {
 @Override public void run() {
 try {
 noteSaver.save(getModel().getNoteId(), getModel().getTitle().get(), getModel().getText().get());
 hideSendProgressAndShowMessage( R.string.note_saved);
 } catch (RetrofitError e) {
 hideSendProgressAndShowMessage( R.string.error_saving_note);
 }
 }
 });
 }
  • 49. GDG DevFest – Milano – October 2015 – @fabioCollini 49 Ui Executor private void hideSendProgressAndShowMessage(final int msg) {
 uiExecutor.execute(new Runnable() {
 @Override public void run() {
 messageManager.showMessage(msg);
 sending.set(false);
 }
 });
 }

  • 50. GDG DevFest – Milano – October 2015 – @fabioCollini 50 Test using single thread @RunWith(MockitoJUnitRunner.class)
 public class NoteViewModelTest {
 
 @Mock NoteView view;
 
 @Mock NoteLoader noteLoader;
 
 @Mock NoteSaver noteSaver;
 
 @Spy Executor executor = new Executor() {
 @Override public void execute(Runnable command) {
 command.run();
 }
 };
 
 @InjectMocks NoteViewModel viewModel;
 
 //...
 }
  • 51. GDG DevFest - Milano - October 2015 - @fabioCollini 51 5Espresso
  • 52. GDG DevFest – Milano – October 2015 – @fabioCollini 52 NoteLoader public class NoteLoader {
 private static NoteLoader instance;
 
 public static NoteLoader singleton() {
 if (instance == null) {
 instance = new NoteLoader();
 }
 return instance;
 }
 
 private NoteLoader() {
 }
 
 @VisibleForTesting
 public static void setInstance(NoteLoader instance) {
 NoteLoader.instance = instance;
 }
 
 //...
 }
  • 53. GDG DevFest – Milano – October 2015 – @fabioCollini public class NoteActivityTest { 
 @Rule public ActivityTestRule<NoteActivity> rule = new ActivityTestRule<>(NoteActivity.class, false, false);
 
 private NoteLoader noteLoader; 
 @Before public void setUp() throws Exception {
 noteLoader = Mockito.mock(NoteLoader.class);
 NoteLoader.setInstance(noteLoader);
 } //...
 } 53 NoteActivityTest
  • 54. GDG DevFest – Milano – October 2015 – @fabioCollini 54 Reload test @Test
 public void testReloadAfterError() {
 when(noteLoader.load())
 .thenThrow( RetrofitError.networkError("url", new IOException()))
 .thenReturn(new Note(123, "aaa", "bbb"));
 
 rule.launchActivity(null);
 
 onView(withText(R.string.retry)).perform(click());
 
 onView(withText(“aaa")) .check(matches(isDisplayed()));
 onView(withText(“bbb")) .check(matches(isDisplayed()));
 }

  • 55. GDG DevFest – Milano – October 2015 – @fabioCollini 55 View ViewModel MockLoader perform(click()) update binding Model request response EspressoTest View ViewModel MockLoaderModelEspressoTest onView verify NoteLoader NoteLoader when().thenReturn() onClick binding
  • 56. GDG DevFest – Milano – October 2015 – @fabioCollini 56 Android Model View ViewModel Activity (or Fragment) is the View All the business logic is in the ViewModel ViewModel is managed using Dependency Injection Model is the Activity (or Fragment) state ViewModel is retained on configuration change ViewModel is testable using a JVM test
  • 57. GDG DevFest – Milano – October 2015 – @fabioCollini 57 Links mockito.org joel-costigliola.github.io/assertj Jay Fields - Working Effectively with Unit Tests Michael Feathers - Working Effectively with Legacy Code medium.com/@fabioCollini/android-data-binding-f9f9d3afc761 github.com/fabioCollini/mv2m github.com/commit-non-javisti/CoseNonJavisteAndroidApp
  • 58. GDG DevFest – Milano – October 2015 – @fabioCollini 58 Thanks for your attention! androidavanzato.it Questions?