-
Notifications
You must be signed in to change notification settings - Fork 45
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
feat: step2 구현 #98
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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() | ||
} | ||
} | ||
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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Before, After 어노테이션을 활용해서 테스트 전후 작업들을 잘 명시해주셨네요 👏 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) 꼭 적용하진 않으셔도 됩니다. 참고만 해주세요! There was a problem hiding this comment. Choose a reason for hiding this commentThe 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
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. MockWebServer를 사용하는 여러 가지 이유가 있겠지만, 큰 장점 중 하나는 MockWebServer를 사용하면 실제 서버가 다운되어도 로컬에서는 테스트가 가능하다는 점입니다. 만약 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. MockWebServer를 이번에 처음 적용을 해 보았는데요. 말씀 주신 내용의 접근 너무 좋은거 같습니다. 지금 인터넷 연결이 끊긴상태에서의 테스트 및 임의 상황에 대해서 테스트를 진행해보았는데요. 실패하네요. 이렇게 실제 데이터가 내려오는 환경을 테스트 하기 위한 라이브러리는 아닌걸까요? |
||
} |
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> | ||
} |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
좋은 정보 공유 감사합니다. 기존에 사내 개발만 하다보니 이런 컨벤션을 놓치고 개발하는 경우가 많았는데 좋은 지표네요