ݺߣ

ݺߣShare a Scribd company logo
Тестирование с Dagger 2
на Android
Виды тестирования
• Простые Unit тесты
• Тестирование Android компонентов
• Instrumentation тесты
public final class SourcesContract {



public interface Presenter extends MVPPresenter<SourcesView> {



void openArticles(final @NonNull NewsSourceDto source);

}



public interface Starter {



void openArticles(final @NonNull NewsSourceDto source);

}



public interface SourcesView extends MVPView {



void setSources(@NonNull List<NewsSourceDto> sources);



void setProgressVisible(boolean visible);



void showError(@NonNull String errorMessage);

}

}
final class SourcesPresenter extends BasePresenter<SourcesContract.View>

implements SourcesContract.Presenter {



@Inject

SourcesPresenter(@NonNull Lazy<SourcesContract.Starter> starter,

@NonNull NewsSourcesDataSource dataSource,

@NonNull @Named(RxModule.COMPUTATION) Scheduler computationScheduler,

@NonNull @Named(RxModule.MAIN) Scheduler observerScheduler) {
…

}
}
public final class NewsSourcesDataSource {



@Inject

public NewsSourcesDataSource(@NonNull NewsSourcesRestService service,
@NonNull @Named(RxModule.NETWORK) Scheduler networkScheduler) {
…
}
…
}
public final class NewsSourcesDataSource {



@Inject

public NewsSourcesDataSource(@NonNull NewsSourcesRestService service,
@NonNull @Named(RxModule.NETWORK) Scheduler networkScheduler) {
…
}
…
}
public interface NewsSourcesRestService {



@NonNull

@GET("sources")

Single<NewsSourcesResponseDto> sources();

}
Unit Тесты
Что используется
Зависимости
testCompile 'org.mockito:mockito-core:2.8.9'

testCompile 'junit:junit:4.12'
@RunWith(MockitoJUnitRunner.class)

public class SourcesPresenterTest {



@Mock SourcesContract.Starter mMockStarter;



@Mock SourcesContract.View mMockView;



@Mock NewsSourcesRestService mMockRestService;



private SourcesPresenter mSourcesPresenter;



@Before

public void init() {

Scheduler testScheduler = …;



Lazy<SourcesContract.Starter> starter = () -> mMockStarter;

NewsSourcesDataSource dataSource =

new NewsSourcesDataSource(mMockRestService, testScheduler);


mSourcesPresenter = new SourcesPresenter(

starter, dataSource, testScheduler, testScheduler);

}

}
@Test

public void test() {
doAnswer(invocation -> Single.just(newNewsSourceDataResponse()))

.when(mMockRestService)
.sources();


mSourcesPresenter.attachView(mMockView);



verify(mMockView, times(1)).setProgressVisible(true);

verify(mMockRestService, times(1)).sources();



verify(mMockView, times(1)).setSources(any());

verify(mMockView, times(1)).setProgressVisible(false);



mSourcesPresenter.onDetachView();

mSourcesPresenter.onDestroyView();

}
Тестирование
компонентов Android SDK
Что используется
Зависимости
testCompile “org.mockito:mockito-core:2.8.9“

testCompile 'junit:junit:4.12'

testCompile "org.robolectric:robolectric:3.3.2"
@RunWith(RobolectricTestRunner.class)

@Config(sdk = Build.VERSION_CODES.LOLLIPOP, constants = BuildConfig.class)

public class SourcesActivityTest {



private SourcesActivity mSourcesActivity;



@Before

public void setup() {
mSourcesActivity = Robolectric.setupActivity(SourcesActivity.class);
}



@Test

public void test() throws Exception {

final View progressView =
mSourcesActivity.findViewById(R.id.progress);

assertThat(progressView.getVisibility(), equalTo(View.GONE));



final RecyclerView sourcesView =
(RecyclerView) mSourcesActivity.findViewById(R.id.list);

assertThat(sourcesView.getVisibility(), equalTo(View.VISIBLE));



final SourceAdapter adapter =
(SourceAdapter) sourcesView.getAdapter();

assertThat(adapter, notNullValue());

assertThat(adapter.getItemCount(), equalTo(1));

final NewsSourceDto source = adapter.getItem(0);

assertThat(source,
equalTo(newNewsSourceDataResponse().getSources().get(0)));



final View sourceView = sourcesView.getChildAt(0);

final TextView sourceNameView =
(TextView) sourceView.findViewById(R.id.name);

assertThat(sourceNameView.getText(), equalTo(source.getName()));

assertThat(sourceNameView.getVisibility(), equalTo(View.VISIBLE));

}

}
Проблемы
1.Необходимо сделать тесты синхронными
2.Необходимо заменить получение данных с
сервера на стабовые
Product Flavors
android {



flavorDimensions ‘mode'


productFlavors {

common {

dimension = 'mode'

}



mock {

dimension = 'mode'

}

}

}
Зависимости
mockCompile “org.mockito:mockito-core:2.8.9“


testCompile 'junit:junit:4.12'

testCompile "org.robolectric:robolectric:3.3.2"
android {



flavorDimensions ‘mode'

productFlavors {

common { dimension = 'mode'}

mock { dimension = 'mode'}

}



variantFilter { variant ->

if (variant.buildType.name != 'debug') {

def names = variant.flavors*.name

if (names.contains('mock')) {

setIgnore(true)

}

}

}

}
Product Flavors
app/src/main/java/com/kirich1409/news/dagger/RxModule.java
app/src/common/java/com/kirich1409/news/dagger/RxModule.java
app/src/mock/java/com/kirich1409/news/dagger/RxModule.java
Mock RxModule
@Module

public abstract class RxModule {



public static final String NETWORK = “network”,
MAIN = “main”,
COMPUTATION = “computation";


@Provides @Named(COMPUTATION)

public static Scheduler provideComputationScheduler() {

return Schedulers.computation();

}



@Provides @Named(NETWORK)

public static Scheduler provideNetworkScheduler() {

return Schedulers.computation();

}



@Provides @Named(MAIN)

public static Scheduler provideMainScheduler() {

return AndroidSchedulers.mainThread();

}

}
Mock NewsApiModule
@Module(includes = NewsNetworkModule.class)

public abstract class NewsApiModule {



@Singleton @Provides

static ArticlesRestService provideArticlesRestService() {

ArticlesRestService mock = Mockito.mock(ArticlesRestService.class);

ArticlesResponseDto responseDto = new ArticlesResponseDto(STATUS_ERROR, "", emptyList(), SORT_LATEST, 0, "");

Mockito.when(mock.articles(anyString(), isNull())).thenReturn(Single.just(responseDto));

return mock;

}



public static NewsSourcesResponseDto newNewsSourceDataResponse() {

NewsSourceDto newsSourceDto = new NewsSourceDto(

"sample_id","Sample news","Sample new for Unit test”, "http://www.sample.org/item.html");

return new NewsSourcesResponseDto(STATUS_OK, singletonList(newsSourceDto), 0, "");

}



@Singleton @Provides

static NewsSourcesRestService provideNewsSourcesRestService() {

NewsSourcesRestService mock = Mockito.mock(NewsSourcesRestService.class);

Mockito.when(mock.sources()).thenReturn(Single.just(newNewsSourceDataResponse()));

return mock;

}

}
Instrumentation тесты
Что используется
Зависимости
mockCompile “org.mockito:mockito-core:2.8.9“
androidTestCompile “org.mockito:mockito-android:2.8.9”

androidTestCompile “cdz.ԻǾ.ܱǰ.ٱ.:-ǰ:2.2.2”
Настройка сборки
android {



defaultConfig {


testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner'



}

}
@RunWith(AndroidJUnit4.class) @LargeTest

public class SourceActivityTest {



@Rule

public IntentsTestRule<SourcesActivity> mActivityRule = new IntentsTestRule<>(SourcesActivity.class);



@Test

public void test() throws Exception {

onView(withId(R.id.list))

.check(matches(childCount(1)))

.check(matches(hasDescendant(withId(R.id.name))));



NewsSourcesResponseDto responseDto = NewsApiModule.newNewsSourceDataResponse();

onView(withId(R.id.name))

.check(matches(withText(responseDto.getSources().get(0).getName())));



onView(withId(R.id.list))

.perform(actionOnItemAtPosition(0, click()));



ComponentName articlesActivity =

new ComponentName(mActivityRule.getActivity(), ArticlesActivity.class);

intended(hasComponent(articlesActivity));

}

}
Espresso
• Core
• Contrib
• Idling Resources
• Intents
• Web
Материалы
• JUnit4
• Mockito
• Basic Guide
• Robolectric
• Android:Getting Started with Testing
• Android Testing Support Library
• Android Espresso
• Android Testing Codelab
Спасибо за внимание

More Related Content

Тестирование на Android с Dagger 2

  • 2. Виды тестирования • Простые Unit тесты • Тестирование Android компонентов • Instrumentation тесты
  • 3. public final class SourcesContract {
 
 public interface Presenter extends MVPPresenter<SourcesView> {
 
 void openArticles(final @NonNull NewsSourceDto source);
 }
 
 public interface Starter {
 
 void openArticles(final @NonNull NewsSourceDto source);
 }
 
 public interface SourcesView extends MVPView {
 
 void setSources(@NonNull List<NewsSourceDto> sources);
 
 void setProgressVisible(boolean visible);
 
 void showError(@NonNull String errorMessage);
 }
 }
  • 4. final class SourcesPresenter extends BasePresenter<SourcesContract.View>
 implements SourcesContract.Presenter {
 
 @Inject
 SourcesPresenter(@NonNull Lazy<SourcesContract.Starter> starter,
 @NonNull NewsSourcesDataSource dataSource,
 @NonNull @Named(RxModule.COMPUTATION) Scheduler computationScheduler,
 @NonNull @Named(RxModule.MAIN) Scheduler observerScheduler) { …
 } }
  • 5. public final class NewsSourcesDataSource {
 
 @Inject
 public NewsSourcesDataSource(@NonNull NewsSourcesRestService service, @NonNull @Named(RxModule.NETWORK) Scheduler networkScheduler) { … } … }
  • 6. public final class NewsSourcesDataSource {
 
 @Inject
 public NewsSourcesDataSource(@NonNull NewsSourcesRestService service, @NonNull @Named(RxModule.NETWORK) Scheduler networkScheduler) { … } … } public interface NewsSourcesRestService {
 
 @NonNull
 @GET("sources")
 Single<NewsSourcesResponseDto> sources();
 }
  • 10. @RunWith(MockitoJUnitRunner.class)
 public class SourcesPresenterTest {
 
 @Mock SourcesContract.Starter mMockStarter;
 
 @Mock SourcesContract.View mMockView;
 
 @Mock NewsSourcesRestService mMockRestService;
 
 private SourcesPresenter mSourcesPresenter;
 
 @Before
 public void init() {
 Scheduler testScheduler = …;
 
 Lazy<SourcesContract.Starter> starter = () -> mMockStarter;
 NewsSourcesDataSource dataSource =
 new NewsSourcesDataSource(mMockRestService, testScheduler); 
 mSourcesPresenter = new SourcesPresenter(
 starter, dataSource, testScheduler, testScheduler);
 }
 }
  • 11. @Test
 public void test() { doAnswer(invocation -> Single.just(newNewsSourceDataResponse()))
 .when(mMockRestService) .sources(); 
 mSourcesPresenter.attachView(mMockView);
 
 verify(mMockView, times(1)).setProgressVisible(true);
 verify(mMockRestService, times(1)).sources();
 
 verify(mMockView, times(1)).setSources(any());
 verify(mMockView, times(1)).setProgressVisible(false);
 
 mSourcesPresenter.onDetachView();
 mSourcesPresenter.onDestroyView();
 }
  • 15. @RunWith(RobolectricTestRunner.class)
 @Config(sdk = Build.VERSION_CODES.LOLLIPOP, constants = BuildConfig.class)
 public class SourcesActivityTest {
 
 private SourcesActivity mSourcesActivity;
 
 @Before
 public void setup() { mSourcesActivity = Robolectric.setupActivity(SourcesActivity.class); }
 
 @Test
 public void test() throws Exception {
 final View progressView = mSourcesActivity.findViewById(R.id.progress);
 assertThat(progressView.getVisibility(), equalTo(View.GONE));
 
 final RecyclerView sourcesView = (RecyclerView) mSourcesActivity.findViewById(R.id.list);
 assertThat(sourcesView.getVisibility(), equalTo(View.VISIBLE));
 
 final SourceAdapter adapter = (SourceAdapter) sourcesView.getAdapter();
 assertThat(adapter, notNullValue());
 assertThat(adapter.getItemCount(), equalTo(1));
 final NewsSourceDto source = adapter.getItem(0);
 assertThat(source, equalTo(newNewsSourceDataResponse().getSources().get(0)));
 
 final View sourceView = sourcesView.getChildAt(0);
 final TextView sourceNameView = (TextView) sourceView.findViewById(R.id.name);
 assertThat(sourceNameView.getText(), equalTo(source.getName()));
 assertThat(sourceNameView.getVisibility(), equalTo(View.VISIBLE));
 }
 }
  • 16. Проблемы 1.Необходимо сделать тесты синхронными 2.Необходимо заменить получение данных с сервера на стабовые
  • 17. Product Flavors android {
 
 flavorDimensions ‘mode' 
 productFlavors {
 common {
 dimension = 'mode'
 }
 
 mock {
 dimension = 'mode'
 }
 }
 }
  • 19. android {
 
 flavorDimensions ‘mode'
 productFlavors {
 common { dimension = 'mode'}
 mock { dimension = 'mode'}
 }
 
 variantFilter { variant ->
 if (variant.buildType.name != 'debug') {
 def names = variant.flavors*.name
 if (names.contains('mock')) {
 setIgnore(true)
 }
 }
 }
 } Product Flavors
  • 22. Mock RxModule @Module
 public abstract class RxModule {
 
 public static final String NETWORK = “network”, MAIN = “main”, COMPUTATION = “computation"; 
 @Provides @Named(COMPUTATION)
 public static Scheduler provideComputationScheduler() {
 return Schedulers.computation();
 }
 
 @Provides @Named(NETWORK)
 public static Scheduler provideNetworkScheduler() {
 return Schedulers.computation();
 }
 
 @Provides @Named(MAIN)
 public static Scheduler provideMainScheduler() {
 return AndroidSchedulers.mainThread();
 }
 }
  • 23. Mock NewsApiModule @Module(includes = NewsNetworkModule.class)
 public abstract class NewsApiModule {
 
 @Singleton @Provides
 static ArticlesRestService provideArticlesRestService() {
 ArticlesRestService mock = Mockito.mock(ArticlesRestService.class);
 ArticlesResponseDto responseDto = new ArticlesResponseDto(STATUS_ERROR, "", emptyList(), SORT_LATEST, 0, "");
 Mockito.when(mock.articles(anyString(), isNull())).thenReturn(Single.just(responseDto));
 return mock;
 }
 
 public static NewsSourcesResponseDto newNewsSourceDataResponse() {
 NewsSourceDto newsSourceDto = new NewsSourceDto(
 "sample_id","Sample news","Sample new for Unit test”, "http://www.sample.org/item.html");
 return new NewsSourcesResponseDto(STATUS_OK, singletonList(newsSourceDto), 0, "");
 }
 
 @Singleton @Provides
 static NewsSourcesRestService provideNewsSourcesRestService() {
 NewsSourcesRestService mock = Mockito.mock(NewsSourcesRestService.class);
 Mockito.when(mock.sources()).thenReturn(Single.just(newNewsSourceDataResponse()));
 return mock;
 }
 }
  • 27. Настройка сборки android {
 
 defaultConfig { 
 testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner'
 
 }
 }
  • 28. @RunWith(AndroidJUnit4.class) @LargeTest
 public class SourceActivityTest {
 
 @Rule
 public IntentsTestRule<SourcesActivity> mActivityRule = new IntentsTestRule<>(SourcesActivity.class);
 
 @Test
 public void test() throws Exception {
 onView(withId(R.id.list))
 .check(matches(childCount(1)))
 .check(matches(hasDescendant(withId(R.id.name))));
 
 NewsSourcesResponseDto responseDto = NewsApiModule.newNewsSourceDataResponse();
 onView(withId(R.id.name))
 .check(matches(withText(responseDto.getSources().get(0).getName())));
 
 onView(withId(R.id.list))
 .perform(actionOnItemAtPosition(0, click()));
 
 ComponentName articlesActivity =
 new ComponentName(mActivityRule.getActivity(), ArticlesActivity.class);
 intended(hasComponent(articlesActivity));
 }
 }
  • 29. Espresso • Core • Contrib • Idling Resources • Intents • Web
  • 30. Материалы • JUnit4 • Mockito • Basic Guide • Robolectric • Android:Getting Started with Testing • Android Testing Support Library • Android Espresso • Android Testing Codelab