Dat testing - An introduction to Java and Android Testing

39
Saul Diaz Android Lead @ Fever Inc. @sefford [email protected] DAT TESTING

Transcript of Dat testing - An introduction to Java and Android Testing

Saul DiazAndroid Lead @ Fever Inc.

@sefford [email protected]

DAT TESTING

"Welcome to the Enrichment Center!"

Why

Testing is about...

Testing is about...

Ctrl

Testing is about...

“At the end of the experiment you will be baked and then there will be… cake.”

Basics

Setup

Exercise

Verify

Teardown

SUT

BLACK BOX

WHITE BOX

public class SaiyanSagaTest { public static final int GOKU_POWER_LEVEL = 10000; public static final String EXPECTED_GOKU_OUTCOME ="It's over nine thousaaaaaaaaand!"; // public Saiyan(KiiSensingInterface sensingMethod) Saiyan goku, vegeta, nappa;

@Before public void setUp() { // KiSense and Scouter classes implement KiSensingInterface goku = new Saiyan(new KiSense()); nappa = new Saiyan(null); vegeta = new Saiyan(new Scouter()); }

@Test public void testVegetaReadsGokusPowerLevel() throws Exception { goku.setPowerLevel(GOKU_POWER_LEVEL); assertThat(EXPECTED_GOKU_OUTCOME, vegeta.scanPowerLevel(goku)); }

@After public void tearDown() { nappa.finish(); }}

STUB

MOCK

Code UnitIntegrati

onAcceptan

ce

“All Aperture technologies remain operational up to 4.000ºK.”

Efficiency

DETERMINISTIC

FAST

EASY

public class Tard { // Initialize rest of Tard, it was awful public String getResponse() { return “nope”; }}

public class TardTest { Tard tard = new Tard(); // Look ma’, I’m testing stuff @Test public void testGetTardResponse() throws Exception { assertEquals(EXPECTED_NOPE, tard.getResponse()); }}

public class Fry{ public void shutUpAndTakeMy(Money money) { if (money != null) shutUpAndTakeIt(money); }}

public class FuturamaTest { @Mock Account fryAccount; @Mock BankFactory bank; @Spy Fry fry; // I don’t want to live with this testing anymore @Test public void testIDontHaveAnyMoneyToTake() throws Exception { when(fryAccount.getMoney()).thenReturn(null); verify(fry, never()).shutUpAndTakeIt(fryAccount.getMoney()); }}

public class Snape {

public int pointsToGryffindor(Student student) { if (Student.DANIEL_RADCLIFFE.equalsTo(student)) return -10; else if (Student.EMMA_WATSON.equalsTo(student)) return Integer.MAX_INT;

return 10; }}

public class HogwartsTest { public static int MAX_POINTS = Integer.MAX_INT; @Spy Snape snape; // Bullshit! 2,147,483,647 points to Gryffindor! @Test public void testSnapeGivingPointsToHermione() throws Exception { when(snape.pointsToGryffindor(((Student) any()))).thenReturn(MAX_POINTS); assertEquals(MAX_POINTS, snape.pointsToGryffindor(Student.EMMA_WATSON)); }}

“Sorry fellas. She’s married. To science.”

Technology

MOCKING & STUBBING

UNIT & INTEGRATION ACCEPTANCE

Mockito JUnit Espresso

JMock Robolectric Robotium

PowerMock CalabashAdditionally JaCoCo can help you on keeping an eye on code coverage.

“Spaaaaaaaaaaaaaaaaaaaace!”

F*** tips!

UNIT F***S EVERYTHING

ELSE*

*Except singletons, UI & race conditions. Those f***ers cannot be tested.

*

* Speak F***ing English too.

public class MarceusWallace { public void initialization(){ // Lots of stuff concerning intialization this.looksLikeABitch = !(Locale.WHAT.equalTo(this.brett.getLocale()) && Locale.ENGLISH.getDisplayLanguage().equalTo( this.brett.getLocale().getDisplayLanguage()) && this.jules.describeMarceusWallace(this.brett) && this.brett.getWhatCounter() < 7); }}

public class MarceusWallaceTest { @Test public void testMrWallaceDoesNotLookLikeABitch() throws Exception { // Testing all cases would lead to 24 cases plus every branch of the method when(brett.getLocale()).thenReturn(whatLocale); when(brett.getWhatCounter).thenReturn(5); when(whatLocale.getDisplayLanguage()) .thenReturn(Locale.ENGLISH.getDisplayLanguage()); when(jules.describeMarceusWallace(brett)).thenReturn(Boolean.TRUE);

controller.initialize(); assertFalse(controller.doesLookLikeABitch()); }}

public class MarceusWallace {

public void initialization(){ // Nice refactor! this.looksLikeABitch = calculateIsLookingLikeABitch(); }}

public class MarceusWallaceControllerTest { @Test public void testMrWallaceDoesNotLookLikeABitch() throws Exception { // Much better! But what if Jules and Brett are final or cannot be mocked // that would be still a lot of boilerplate! when(controller.calculateIsLookingLikeABitch() .thenReturn(Boolean.FALSE);

controller.initialize(); assertFalse(controller.doesLookLikeABitch()); }}

public class MarceusWallace {

public void initialization(){ // Now I can test calculateIsLooking injecting its dependencies! this.looksLikeABitch = calculateIsLookingLikeABitch(this.brett, this.jules); }}

public class MarceusWallaceControllerTest { @Test public void testMrWallaceDoesNotLookLikeABitch() throws Exception { // Using a spy and injection I can narrow the SUT to the initialization // method. We still have to do 24 cases for testCalculateIsLooking... when(controller.calculateIsLookingLikeABitch(brett, jules)) .thenReturn(Boolean.FALSE); controller.initialize(); assertFalse(controller.doesLookLikeABitch()); }}

public class GruePresenter {

public GruePresenter(Context context){ Crashlytics.start(context); Log.d("GruePresenter", "Crashlytics Initialized"); // Even if you injected this. It would still be wrong mixpanelAPI = MixpanelAPI.getInstance(context, MIXPANEL_TOKEN); new YourFavoriteAsyncTask().execute(new DoSomethingNetworky()); throws new FacepalmException("You got eaten by a Grue!"); }}

public class GruePresenterTest {

@Test(expects = FacepalmException.class) public void testInitialization() throws Exception { GruePresenter presenter = new GruePresenter(Robolectric.application); // Your code is baaaaaaad and you should feel baaaaaaaad }}

public class GruePresenter { // Remember, AsyncTask.execute is final! public void doSomethingAsyncish(){ new YourFavoriteAsyncTask() .execute(new DoSomethingNetworky()); }}

public class CrashlyticsAdapter { public void start(Context context) { Crashlytics.start(context); }}

public class Logger { public void debug(String tag, String msg){ Log.d(tag, msg); }}

public class MixpanelAdapter { // You can inject an instance of Mixpanel public MixpanelWrapper(MixpanelAPI api) { this.api = api; }}

public class GruePresenter {

public GruePresenter(CraslyticsWrapper crashlytics, Logger logger, MixpanelWrapper mixpanelWapper){ crashlytics.start(context); logger.debug("GruePresenter", "Crashlytics Initialized"); this.mixpanelWrapper = mixpanelWrapper; // We had to move the doSomethingAsyncish out of here }}

public class GruePresenterTest {

@Test public void testInitialization() throws Exception { GruePresenter presenter = new GruePresenter(mockCrash, mockLogger, mockMixpanel); doNothing().when(presenter).doSomethingAsyncIsh() // Alright, let’s dance! }}

“Aperture Science: We do what we must, because we can.”

Rolling

2.397 Tests● 2.364 units● 33 integration● 1.942 Android -

coupled

~2:30m full test suite

~70% Coverage

Maven 3.2.3● JUnit● Mockito● Robolectric

59 users, 7 crashes

~35K lines of code

1. We are paid to solve problems. Be efficient.

2. Tests test what your code does, not what it should do.

3. If it is not your code, there is no need to test it.

“Let me answer that question with a question: Who wants to make 60$?”

Questions?

● xUnit Test Patterns: Refactoring Test Code, Gerard Meszaros

o http://xunitpatterns.com/

● 467 tests, 0 failures, 0 confidence, Katrina Owen● Mocks aren’t stubs, Martin Fowler● Eveyrthing you wanted to know about testing, but were afraid to ask, Jorge

Barroso● Exploding Software-Engineering Myths, Janie Chang

Bibliography

“So you know what? You win. Just go.”

Thanks!