Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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
31 changes: 29 additions & 2 deletions .github/workflows/check_workflow.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: Run Gradle on PRs
on: pull_request
jobs:
gradle:
ktlint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
Expand All @@ -12,5 +12,32 @@ jobs:
- name: Change Permission
run: chmod +x ./gradlew

- name: Execute Gradle check
- name: Execute Ktlint Check
run: ./gradlew ktlintCheck

test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup Gradle
uses: gradle/gradle-build-action@v2

- name: Change Permission
run: chmod +x ./gradlew

- name: Execute Unit Tests
run: ./gradlew test jacocoTestReport

- name: Upload Test Results
uses: actions/upload-artifact@v4
if: always()
with:
name: test-results
path: app/build/test-results

- name: Upload Coverage Report
uses: actions/upload-artifact@v4
with:
name: coverage-report
path: app/build/reports/jacoco
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,19 @@
./gradlew ktlintCheck
```

### Unitテストについて

- Hilt, JUnit, Mockitoを持ちいてUnitテストを作成しました。

```bash
# 全件実行をする方法
./gradlew test

# 単体で動かす方法
./gradlew :app:testDebugUnitTest --tests "jp.co.yumemi.android.code_check.features.github.GitHubServiceRepositoryImplTest"
```


### 動作

1. 何かしらのキーワードを入力
Expand Down
3 changes: 3 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ dependencies {
androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
implementation "com.google.dagger:hilt-android:2.55"
kapt "com.google.dagger:hilt-compiler:2.55"
testImplementation "org.mockito:mockito-core:5.2.0"
testImplementation 'org.mockito:mockito-inline:5.2.0'


// Retrofit
implementation("com.squareup.retrofit2:converter-moshi:2.9.0")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package jp.co.yumemi.android.code_check.features.github

import jp.co.yumemi.android.code_check.features.github.api.GitHubServiceApi
import jp.co.yumemi.android.code_check.features.github.entity.RepositoryItem
import jp.co.yumemi.android.code_check.features.github.entity.RepositoryList
import jp.co.yumemi.android.code_check.features.github.entity.RepositoryOwner
import jp.co.yumemi.android.code_check.features.github.reposiotory.GitHubServiceRepositoryImpl
import jp.co.yumemi.android.code_check.features.github.utils.GitHubError
import jp.co.yumemi.android.code_check.features.github.utils.NetworkResult
import kotlinx.coroutines.runBlocking
import org.json.JSONException
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.mockito.Mockito.mock
import org.mockito.Mockito.`when`
import retrofit2.HttpException
import java.io.IOException

class GitHubServiceRepositoryImplTest {
private lateinit var api: GitHubServiceApi
private lateinit var repository: GitHubServiceRepositoryImpl

@Before
fun setUp() {
api = mock(GitHubServiceApi::class.java)
repository = GitHubServiceRepositoryImpl(api)
}

@Test
fun `fetchSearchResults returns success with valid data`() =
runBlocking {
// Arrange
val mockResponse = mockRepositoryList()
`when`(api.getRepository("test")).thenReturn(mockResponse)

// Act
val result = repository.fetchSearchResults("test")

// Assert
assert(result is NetworkResult.Success)
val success = result as NetworkResult.Success
assertEquals(2, success.data.size)
assertEquals("repo1", success.data[0].name)
assertEquals("owner1", success.data[0].ownerIconUrl)
}

@Test
fun `fetchSearchResults returns error on HttpException`() =
runBlocking {
// Arrange
val httpException = mock(HttpException::class.java)
`when`(httpException.code()).thenReturn(401)
`when`(api.getRepository("test")).thenThrow(httpException)

// Act
val result = repository.fetchSearchResults("test")

// Assert
assert(result is NetworkResult.Error)
val error = result as NetworkResult.Error
assert(error.exception is GitHubError.AuthenticationError)
}

@Test
fun `fetchSearchResults returns error on IOException`() =
runBlocking {
// Arrange
`when`(api.getRepository("test")).thenAnswer {
throw IOException("Network error")
}

// Act
val result = repository.fetchSearchResults("test")

// Assert
assert(result is NetworkResult.Error)
val error = result as NetworkResult.Error
assert(error.exception is GitHubError.NetworkError)
}

@Test
fun `fetchSearchResults returns error on JSONException`() =
runBlocking {
// Arrange
`when`(api.getRepository("test")).thenAnswer {
throw JSONException("Parsing error")
}

// Act
val result = repository.fetchSearchResults("test")

// Assert
assert(result is NetworkResult.Error)
val error = result as NetworkResult.Error
assert(error.exception is GitHubError.ParseError)
}

// Mockデータ生成関数
private fun mockRepositoryList(): RepositoryList {
return RepositoryList(
items =
listOf(
RepositoryItem(
name = "repo1",
owner = RepositoryOwner("owner1"),
language = "Kotlin",
stargazersCount = 100,
watchersCount = 50,
forksCount = 20,
openIssuesCount = 5,
),
RepositoryItem(
name = "repo2",
owner = RepositoryOwner("owner2"),
language = "Java",
stargazersCount = 200,
watchersCount = 80,
forksCount = 30,
openIssuesCount = 10,
),
),
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package jp.co.yumemi.android.code_check.features.github

import jp.co.yumemi.android.code_check.core.api.NetworkConnectivityService
import jp.co.yumemi.android.code_check.core.entity.RepositoryItem
import jp.co.yumemi.android.code_check.features.github.reposiotory.GitHubServiceRepository
import jp.co.yumemi.android.code_check.features.github.reposiotory.NetworkException
import jp.co.yumemi.android.code_check.features.github.usecase.GitHubServiceUsecaseImpl
import jp.co.yumemi.android.code_check.features.github.utils.NetworkResult
import kotlinx.coroutines.runBlocking
import org.junit.Assert.assertEquals
import org.junit.Assert.assertThrows
import org.junit.Before
import org.junit.Test
import org.mockito.Mockito.mock
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations

class GitHubServiceUsecaseImplTest {
private lateinit var repository: GitHubServiceRepository
private lateinit var networkConnectivityService: NetworkConnectivityService
private lateinit var usecase: GitHubServiceUsecaseImpl

@Before
fun setUp() {
MockitoAnnotations.openMocks(this)
repository = mock(GitHubServiceRepository::class.java)
networkConnectivityService = mock(NetworkConnectivityService::class.java)
usecase = GitHubServiceUsecaseImpl(repository, networkConnectivityService)
}

@Test
fun `fetchSearchResults throws NetworkException when offline`() {
`when`(networkConnectivityService.isNetworkAvailable()).thenReturn(false)

val exception =
assertThrows(NetworkException::class.java) {
runBlocking {
usecase.fetchSearchResults("test")
}
}

assertEquals("オフライン状態です", exception.message)
}

@Test
fun `fetchSearchResults returns results when online`() =
runBlocking {
val mockResults =
listOf(
RepositoryItem(
name = "repo1",
ownerIconUrl = "description1",
language = "url1",
stargazersCount = 100,
forksCount = 50,
openIssuesCount = 30,
watchersCount = 70,
),
RepositoryItem(
name = "repo2",
ownerIconUrl = "description2",
language = "url2",
stargazersCount = 100,
forksCount = 50,
openIssuesCount = 30,
watchersCount = 70,
),
)
`when`(networkConnectivityService.isNetworkAvailable()).thenReturn(true)
`when`(repository.fetchSearchResults("test")).thenReturn(NetworkResult.Success(mockResults))

val result = usecase.fetchSearchResults("test")

assertEquals(NetworkResult.Success(mockResults), result)
}
}
Loading