TESTING
Tomek Polanskitpolansk
WHY?WHEN?HOW?
“Too much time pressure with making visible changes.
Customer doesn't appreciate tests enough.”
“To make iterations as fast as possible I would not use tests for
prototypes or early MVPs.”
“Specs are too volatile, tests would just need to be updated
constantly.”
“Writing tests is hard, they might be difficult to understand.”
“Nobody else in the project uses them.
They weren't done or were broken when I got involved.”
Time and budget“Too much time
pressure with making visible changes”
Instant changes Return on investment
vs
Speed
“Not for prototypes or early MVPs”
Time
Prog
ress
without tests
with tests
Prototype
Minimum Viable Product
Lovable
by Henrik Kniberg
Constant ChangeThe
“Specs are too volatile”
Programming is hard
https://deeguns.files.wordpress.com/2013/07/computerman.jpg
urbanattitude.fr/wp-content/uploads/2015/03/BER_08.jpg
Architecture
“Tests are hard and difficult to understand”
Maintenance
“Missing/broken tests”
What should unit tests look like?
Fast
Reliable
Simple to understand@Testpublic void get_whenElementExists() { List<String> list = Collections.singletonList(DEFAULT);
assertThat(get(list, 0)).isEqualTo(DEFAULT);}
@Testpublic void get_whenIndexTooBig_returnNull() { List<String> list = Collections.singletonList(DEFAULT);
assertThat(get(list, 1)).isNull();}
@Testpublic void get_whenIndexTooSmall_returnNull() { List<String> list = Collections.singletonList(DEFAULT);
assertThat(get(list, -1)).isNull();}
Simple to understand?
Arrange Act Assert
@Testpublic void insertBuilder_withTable_withColumn_doesNotThrow() { new Builder().table(TABLE) .addColumn(COLUMN1) .build();}
@Test(expected = IllegalStateException.class)public void insertBuilder_withoutColumns_throwsException() { new Builder().table(TABLE) .build();}
Similar tests
Test setup@Testpublic void test() { List<Language> list = Arrays.asList(GERMAN, ENGLISH); Mockito.when(mDataModel.getSupportedLanguages()) .thenReturn(list); Mockito.when(mDataModel.getGreetingByLanguageCode(LanguageCode.EN)) .thenReturn("Hi!");
// The rest of the test}
Arrange builderclass ArrangeBuilder {
ArrangeBuilder withLanguages(Language... languages) { Mockito.when(mDataModel.getSupportedLanguages()) .thenReturn(Arrays.asList(languages)); return this; }
ArrangeBuilder withGreetings(LanguageCode code, String greeting) { Mockito.when(mDataModel.getGreetingByLanguageCode(code)) .thenReturn(greeting); return this; }}
@Testpublic void test() { List<Language> list = Arrays.asList(GERMAN, ENGLISH); Mockito.when(mDataModel.getSupportedLanguages()) .thenReturn(list); Mockito.when(mDataModel.getGreetingByLanguageCode(LanguageCode.EN)) .thenReturn("Hi!");
// The rest of the test}
@Testpublic void test() { new ArrangeBuilder() .withLanguages(GERMAN, ENGLISH) .withGreetings(LanguageCode.EN, "Hi!");
// The rest of the test}
Arrange builder
Simple to write
Is it all?
Do I have enough tests?
static boolean isEven(int number) { return number % 2 == 0;}
static boolean isEven(int number) { return number % 2 == 0 ? true : false;}
What’s the code coverage?
@Testpublic void isTwoEven() { assertThat(isEven(2))
.isTrue();}
static boolean isEven(int number) { if ( number % 2 == 0 ) { return true; } else { return false; }}
What’s the code coverage?Observable<Boolean> isEven(int number) { return Observable.just(number) .map(num -> num % 2 == 0);}
@Testpublic void isTwoEven() { TestSubscriber<Boolean> ts = new TestSubscriber<>();
isEven(2).subscribe(ts);
ts.assertValue(true);}
@Testpublic void isTwoEven() { TestSubscriber<Boolean> ts = new TestSubscriber<>();
isEven(2).subscribe(ts);}
Do I have too many tests?
Do I have too much code?
public class Translation {
public final String mText;
public Translation(String text) { mText = text; }
public String text() { return mText; }} @AutoValue
public abstract class Translation {
public abstract String text(); public static Translation create(String text) { return new AutoValue_Translation(text); }}
. public class Translation {
private final String mText;
public Translation(String text) { mText = text; }
public String text() { return mText; }
@Override public int hashCode() { return mText != null ? mText.hashCode() : 0; }
@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false;
Translation that = (Translation) o;
return mText != null ? mText.equals(that.mText) : that.mText == null; }}
. @Testpublic void valueEquality() { String value = "text"; Translation first = Translation.create(value); Translation second = Translation.create(value); assertThat(first).isEqualTo(second);}
@AutoValuepublic abstract class Translation {
public abstract String text(); public static Translation create(String text) { return new AutoValue_Translation(text); }
@Override public int hashCode() { // ... }
@Override public boolean equals(Object o) { // ... }
}
What is this code here?
Observable<String> getCurrentLanguage() { return getSystemLanguage() .skip(1) .first();}
Observable<String> getCurrentLanguage() { return getSystemLanguage() // .skip(1) .first();}
Test Driven Development
Less Code Less Tests Peace of mind
Be playful Don’t be dogmatic
Find a test mentor
“Everyone knows that debugging is twice as hard as writing a program in the first place.
So if you're as clever as you can be when you write it, how will you
ever debug it?”Brian Kernighan
Thank you!
tpolansk