Slide 1

Slide 1 text

Clean Architecture & TDD @fushiroyama

Slide 2

Slide 2 text

About Me • Fumihiko Shiroyama • Android App Developer • Unit Test Enthusiast • https://github.com/srym

Slide 3

Slide 3 text

Clean Architecture • Presentation • Domain • Infrastructure

Slide 4

Slide 4 text

TDD • Test Driven Development • Test First • Minimum Implementation • Refactoring

Slide 5

Slide 5 text

TDD is great! because... • Focus on I/O • Less reworking • Force Unit Testing

Slide 6

Slide 6 text

Example • Infrastructure • Remote Data Source • GitHub Information • Local Unit Test

Slide 7

Slide 7 text

Interface public interface RemoteGitHubDataSource { Single> listRepos(@NonNull String user); }

Slide 8

Slide 8 text

Blank Implementation public class RestGitHubDataSource implements RemoteGitHubDataSource { @Override public Single> listRepos(@NonNull String user) { return Single.error(new RuntimeException()); } }

Slide 9

Slide 9 text

Blank Implementation public class RestGitHubDataSource implements RemoteGitHubDataSource { @Override public Single> listRepos(@NonNull String user) { return Single.error(new RuntimeException()); } }

Slide 10

Slide 10 text

Create Test • Mouse over class • Alt + Enter • Create Test

Slide 11

Slide 11 text

Create Test

Slide 12

Slide 12 text

Create Test

Slide 13

Slide 13 text

By the way... • Mouse over class • Shift + Command + T • Choose Test

Slide 14

Slide 14 text

Test First public class RestGitHubDataSourceTest { private RestGitHubDataSource dataSource; @Before public void setUp() throws Exception { dataSource = new RestGitHubDataSource(); } @Test public void listRepos() throws Exception { // implement here! } }

Slide 15

Slide 15 text

Test First @Test public void listRepos() throws Exception { List repos = restGitHubDataSource.listRepos("srym") .test() .await() .assertNoErrors() .assertComplete() .values() .get(0); assertThat(repos).isNotNull(); }

Slide 16

Slide 16 text

Test First assertThat(repos).isNotNull().isNotEmpty(); Repo repo = repos.get(0); assertThat(repo).isNotNull(); assertThat(repo.getFullName()).isNotBlank(); assertThat(repo.getId()).isGreaterThanOrEqualTo(0); assertThat(repo.getOwner()).isNotNull();

Slide 17

Slide 17 text

Test First assertThat(repos).isNotNull().isNotEmpty(); Repo repo = repos.get(0); assertThat(repo).isNotNull(); assertThat(repo.getFullName()).isNotBlank(); assertThat(repo.getId()).isGreaterThanOrEqualTo(0); assertThat(repo.getOwner()).isNotNull(); You can confirm the specs BEFORE you implement

Slide 18

Slide 18 text

Test Execution (failure) • This of course fails.

Slide 19

Slide 19 text

Minimum Implementation public class RestGitHubDataSource implements RemoteGitHubDataSource { private final GitHubService gitHubService; @Inject public RestGitHubDataSource(GitHubService gitHubService) { this.gitHubService = gitHubService; } @Override public Single> listRepos(@NonNull String user) { return gitHubService.listRepos(user); } }

Slide 20

Slide 20 text

Minimum Implementation public class RestGitHubDataSource implements RemoteGitHubDataSource { private final GitHubService gitHubService; @Inject public RestGitHubDataSource(GitHubService gitHubService) { this.gitHubService = gitHubService; } @Override public Single> listRepos(@NonNull String user) { return gitHubService.listRepos(user); } }

Slide 21

Slide 21 text

Minimum Implementation public class RestGitHubDataSource implements RemoteGitHubDataSource { private final GitHubService gitHubService; @Inject public RestGitHubDataSource(GitHubService gitHubService) { this.gitHubService = gitHubService; } @Override public Single> listRepos(@NonNull String user) { return gitHubService.listRepos(user); } }

Slide 22

Slide 22 text

But wait... • This is Local Unit Test • Mock data? Hmm.

Slide 23

Slide 23 text

MockWebServer

Slide 24

Slide 24 text

MockWebServer • Provided by OkHttp • Full HTTP Stack • Can test REAL response

Slide 25

Slide 25 text

MockWebServer private final MockWebServer mockWebServer = new MockWebServer();

Slide 26

Slide 26 text

MockWebServer Dispatcher dispatcher = new Dispatcher() { @Override public MockResponse dispatch(RecordedRequest request) throws InterruptedException { return new MockResponse().setResponseCode(404); } }; mockWebServer.setDispatcher(dispatcher); mockWebServer.start();

Slide 27

Slide 27 text

MockWebServer @Override public MockResponse dispatch(RecordedRequest request) throws InterruptedException { if (request == null || request.getPath() == null) { return new MockResponse().setResponseCode(400); } if (request.getPath().matches("/users/.+/repos")) { return new MockResponse() .setBody(readJsonFromResources("users_repos.json")) .setResponseCode(200); } return new MockResponse().setResponseCode(404); }

Slide 28

Slide 28 text

MockWebServer @Override public MockResponse dispatch(RecordedRequest request) throws InterruptedException { if (request == null || request.getPath() == null) { return new MockResponse().setResponseCode(400); } if (request.getPath().matches("/users/.+/repos")) { return new MockResponse() .setBody(readJsonFromResources("users_repos.json")) .setResponseCode(200); } return new MockResponse().setResponseCode(404); }

Slide 29

Slide 29 text

MockWebServer @Override public MockResponse dispatch(RecordedRequest request) throws InterruptedException { if (request == null || request.getPath() == null) { return new MockResponse().setResponseCode(400); } if (request.getPath().matches("/users/.+/repos")) { return new MockResponse() .setBody(readJsonFromResources("users_repos.json")) .setResponseCode(200); } return new MockResponse().setResponseCode(404); }

Slide 30

Slide 30 text

MockWebServer @Override public MockResponse dispatch(RecordedRequest request) throws InterruptedException { if (request == null || request.getPath() == null) { return new MockResponse().setResponseCode(400); } if (request.getPath().matches("/users/.+/repos")) { return new MockResponse() .setBody(readJsonFromResources("users_repos.json")) .setResponseCode(200); } return new MockResponse().setResponseCode(404); } Talk about this later

Slide 31

Slide 31 text

MockWebServer Retrofit retrofit = new Retrofit.Builder() .baseUrl(mockWebServer.url("")) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .addConverterFactory(GsonConverterFactory.create()) .build(); GitHubService gitHubService = retrofit.create(GitHubService.class);

Slide 32

Slide 32 text

MockWebServer Retrofit retrofit = new Retrofit.Builder() .baseUrl(mockWebServer.url("")) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .addConverterFactory(GsonConverterFactory.create()) .build(); GitHubService gitHubService = retrofit.create(GitHubService.class);

Slide 33

Slide 33 text

Prepare Data • Curl • Postman curl https://api.github.com/users/srym/repos > users_repos.json

Slide 34

Slide 34 text

Prepare Data • Put it test/resources

Slide 35

Slide 35 text

Read JSON from file private String readJsonFromResources(@NonNull String fileName) { InputStream inputStream = getClass().getClassLoader().getResourceAsStream(fileName); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); StringBuilder stringBuilder = new StringBuilder(); try { String buffer; while ((buffer = bufferedReader.readLine()) != null) { stringBuilder.append(buffer); } } catch (IOException e) { fail(e.getMessage(), e); } return stringBuilder.toString(); }

Slide 36

Slide 36 text

Fix Test @Before public void setUp() throws Exception { // abbr. dataSource = new RestGitHubDataSource(gitHubService); } @Test public void listRepos() throws Exception { List repos = restGitHubDataSource.listRepos("srym") .test() .await() .assertNoErrors() .assertComplete() .values() .get(0); assertThat(repos).isNotNull(); // abbr. }

Slide 37

Slide 37 text

Passed!

Slide 38

Slide 38 text

Refactoring • TDD is NOT perfect • Repeat Write & Test

Slide 39

Slide 39 text

Cons • Useless when API changes • Takes longer time

Slide 40

Slide 40 text

Pros • Quality • Relief • Takes shorter time in total

Slide 41

Slide 41 text

TDD rocks!

Slide 42

Slide 42 text

Thank You

Slide 43

Slide 43 text

Links • https://github.com/srym/Architecture • https://github.com/square/okhttp/tree/ master/mockwebserver • http://www.irasutoya.com/