writing testable android apps
Post on 16-Jan-2017
287 Views
Preview:
TRANSCRIPT
What does it mean testable?
• Test it quickly during development
• Test units in isolation
• There’s a way to provide mocks
“Standard” Android app
• check Android SDK samples & documentation
• or Google’s iosched app
• business logic in Activities, Fragments, Services, …
• view and presenter effectively mixed together
Android APIs
• difficult to test and mock
• we don’t have creation of Android components under control
Abstracting Android APIs
• we need some boilerplate to get away
• remove all business logic from Android component classes
• isolate business logic into presenters, managers, POJOs, …
• when possible use MVP pattern
Clean code
• try to write clean code
• avoid methods having hundreds of lines
• avoid Activities/Fragments having thousands of lines
And how to test it?
• our presenters, POJOs, managers, providers, etc. are simple to test
• Android components are stripped off of business logic
• nothing important to test remains there
Android Unit Tests
• Added rather recently
• You need some work to make them really useful.
• Dependency injection
• Mocking
Dagger 2• by Google
• evolution of Dagger 1 (by Square)
• no reflection
• generated code in compile time
• constructor and field injection
Dagger 2
• Constructor injection
private ProviderC mProviderC; private ProviderD mProviderD; @Inject public ProviderA(ProviderC providerC, ProviderD providerD) { mProviderC = providerC; mProviderD = providerD; }
Dagger 2• Field injection
public class MainActivity extends AppCompatActivity { @Inject ProviderA mProviderA; @Inject ProviderB mProviderB;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ((MyApp) getApplication()).getAppComponent() .injectMainActivity(this); }}
Dagger 2
• prefer constructor injection wherever possible
• you can test the unit in isolation
• providing mocks is a piece of cake
Dagger 2• Some catches when using field injection
• Construct the whole component
• Implicit provisions doesn’t support overrides!!
public class ProviderA { … @Inject public ProviderA(ProviderC providerC) { mProviderC = providerC; }}
Dagger 2• Some catches when using field injection
• Make explicit provisions @Module public class AppModule { @Provides @Singleton ProviderB provideProvider2(ProviderC providerC) { return new ProviderB(providerC); } @Provides ProviderD provideProvider4() { return new ProviderD(); } }
• Beware marking constructors with @Inject when providing explicitly
• may create unwanted double provision
Mocking
• Android framework has some historical things
• package android.test.mock
• for instrumentation tests
Android APIs & Unit Test
• historically
• java.lang.RuntimeException: Stub!
• now
• mockable android.jar
• Method ... not mocked.
@RunWith(JUnit4.class) public class ProviderATest { @Mock ProviderC mProviderC; @Mock ProviderD mProviderD; ProviderA mProviderA; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); Mockito.when(mProviderC.getString()).thenReturn("hello"); Mockito.when(mProviderD.getString()).thenReturn("world"); mProviderA = new ProviderA(mProviderC, mProviderD); }
…}
Static methods• avoid it as much as possible
• things like TextUtils.isEmpty() can be painful
• alternatives, e.g. Apache commons StringUtils.isEmpty()
• PowerMock to the rescue
• still better to avoid static
Negatives?
• increase the number of methods and classes
• duplication of Android interfaces/classes
• bloated constructors with lots of dependencies
Dev App Builds
• How to use dev overrides in dev buils?
• utilize Android’s Gradle build
• Product Flavors can be used for that
top related