Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: step2 구현 #98

Merged
merged 1 commit into from
Aug 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package camp.nextstep.edu.github.data.network

import camp.nextstep.edu.github.domain.GithubRepository
import retrofit2.http.GET

interface GitHubService {
@GET("/repositories")
suspend fun getRepositories(): List<GithubRepository>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package camp.nextstep.edu.github.data.network

import camp.nextstep.edu.github.domain.GithubRepository
import camp.nextstep.edu.github.domain.network.GitHubDataSource

internal class GithubRepositoriesDataSource(
private val gitHubService: GitHubService
) : GitHubDataSource {
override suspend fun fetchRepositories(): List<GithubRepository> {
return gitHubService.getRepositories()
}
}
Comment on lines +6 to +12
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

관습적으로 interface 구현체에 대한 네이밍을 ~Impl로 짓는 경우들도 있는데, 의미있는 이름을 잘 지어주셨군요.

구글에서 다음과 같이 가이드하는 경우도 있어서 참고차 남깁니다. 🙂
https://developer.android.com/topic/architecture/recommendations#naming-conventions

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

좋은 정보 공유 감사합니다. 기존에 사내 개발만 하다보니 이런 컨벤션을 놓치고 개발하는 경우가 많았는데 좋은 지표네요

Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package camp.nextstep.edu.github.data.network

import camp.nextstep.edu.github.domain.GithubRepository
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.test.runTest
import okhttp3.OkHttpClient
import okhttp3.mockwebserver.MockResponse
import okhttp3.mockwebserver.MockWebServer
import org.junit.After
import org.junit.Before
import org.junit.Test
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.io.File
import java.util.concurrent.TimeUnit

class GithubRepositoriesDataSourceTest {
private lateinit var mockWebServer: MockWebServer
private lateinit var api: GitHubService
private lateinit var dataSource: GithubRepositoriesDataSource

private val client = OkHttpClient.Builder()
.connectTimeout(1, TimeUnit.SECONDS)
.readTimeout(1, TimeUnit.SECONDS)
.writeTimeout(1, TimeUnit.SECONDS)
.build()

@Before
fun setUp() {
mockWebServer = MockWebServer()
}

@After
fun tearDown() {
mockWebServer.shutdown()
}

@Test
fun `GitHub repository들을 불러옴`() = runTest {
api = Retrofit.Builder()
.baseUrl(mockWebServer.url(""))
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(GitHubService::class.java)
Comment on lines +40 to +45
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before, After 어노테이션을 활용해서 테스트 전후 작업들을 잘 명시해주셨네요 👏
api, dataSource를 초기화하는 로직도 setUp으로 옮기면 어떨까요?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그렇게 하려고 했었는데 url에 실제 데이터를 넣는 식으로 테스트를 진행하려고하다 보니 이렇게 작성이 되었습니다.

val body = File("src/test/resources/response.json").readText()
val response = MockResponse().setBody(body).setResponseCode(200)
mockWebServer.enqueue(response)
dataSource = GithubRepositoriesDataSource(api)
val actual = dataSource.fetchRepositories()[0]
val expected = GithubRepository("nextStep", "android-github")
assertThat(actual).isEqualTo(expected)
Comment on lines +46 to +52
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

검증을 위한 테스트를 잘 작성해주셨습니다.

혹시 Given-When-Then 스펙에 대해 들어본적이 있으신가요? 테스트도 결국에는 내가 짠 프로그램을 검증하기 위한 하나의 시나리오의 역할을 하는 경우가 많은데요, 이러한 Given-When-Then 스펙을 활용하면 단계별로 명확하게 시나리오를 관리할 수 있습니다.

다음과 같은 형태로 단순 나누기만 해도 시나리오가 명확해질 수 있습니다.

// given
val body = File("src/test/resources/response.json").readText()
val response = MockResponse().setBody(body).setResponseCode(200)

// when
mockWebServer.enqueue(response)
val actual = dataSource.fetchRepositories()[0]

// then
val expected = GithubRepository("nextStep", "android-github")
assertThat(actual).isEqualTo(expected)

꼭 적용하진 않으셔도 됩니다. 참고만 해주세요!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

말씀하신 내용 너무 좋은거 같습니다. TDD랑 너무 거리가 멀게 살다 보니 TDD에 있어서는 완전 초짜라 많은 정보 가감없이 부탁드리겠습니다 ^^

}

@Test
fun `GitHub repository 실제 데이터 체크`() = runTest {
api = Retrofit.Builder()
.baseUrl(mockWebServer.url("https://api.github.com/"))
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(GitHubService::class.java)
dataSource = GithubRepositoriesDataSource(api)
val actual = dataSource.fetchRepositories()
assertThat(actual).hasSize(100)
}
Comment on lines +55 to +66
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MockWebServer를 사용하는 여러 가지 이유가 있겠지만, 큰 장점 중 하나는 MockWebServer를 사용하면 실제 서버가 다운되어도 로컬에서는 테스트가 가능하다는 점입니다. 만약 https://api.github.com/가 다운되면 이 테스트도 실패할까요?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MockWebServer를 이번에 처음 적용을 해 보았는데요. 말씀 주신 내용의 접근 너무 좋은거 같습니다. 지금 인터넷 연결이 끊긴상태에서의 테스트 및 임의 상황에 대해서 테스트를 진행해보았는데요. 실패하네요. 이렇게 실제 데이터가 내려오는 환경을 테스트 하기 위한 라이브러리는 아닌걸까요?

}
4 changes: 4 additions & 0 deletions core/data/src/test/resources/response.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[{
"full_name": "nextStep",
"description": "android-github"
}]
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package camp.nextstep.edu.github.domain

import com.google.gson.annotations.SerializedName

data class GithubRepository(
@SerializedName("full_name")
val fullName: String,
val description: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package camp.nextstep.edu.github.domain.network

import camp.nextstep.edu.github.domain.GithubRepository

interface GitHubDataSource {
suspend fun fetchRepositories(): List<GithubRepository>
}