Skip to content

Commit 238e9f6

Browse files
committed
Merged in feature/CM-1074 (pull request #4)
Android: Integrated KTOR. Approved-by: Ananthakrishna Bhaskar
2 parents 4d45cbe + 809032d commit 238e9f6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1563
-214
lines changed

app/build.gradle.kts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ plugins {
44
kotlin("kapt")
55
id("com.google.dagger.hilt.android")
66
id("jacoco-reports")
7+
id("org.jetbrains.kotlin.plugin.serialization")
78
}
89

910
android {
@@ -55,6 +56,7 @@ android {
5556
}
5657

5758
dependencies {
59+
androidTestImplementation(project(mapOf("path" to ":app")))
5860
val androidLib = ytemplate.android.build.AndroidLib
5961
val testLib = ytemplate.android.build.TestLib
6062
implementation (androidLib.CORE_KTX)
@@ -77,17 +79,28 @@ dependencies {
7779
//coroutine
7880
implementation(androidLib.COROUTINE)
7981
testImplementation(androidLib.COROUTINE_TEST)
82+
testImplementation(androidLib.COROUTINE_TURBINE)
8083
//Room
8184
implementation(androidLib.ROOM_RUNTIME)
8285
annotationProcessor(androidLib.ROOM_COMPILER)
8386
kapt(androidLib.ROOM_COMPILER)
8487
implementation(androidLib.ROOM_KTX)
8588
testImplementation(androidLib.ROOM_TEST)
89+
//Ktor
90+
implementation(androidLib.KTOR_CORE)
91+
implementation(androidLib.KTOR_CLIENT)
92+
implementation(androidLib.KTOR_CONTENT_NEGOTIATION)
93+
implementation(androidLib.KTOR_SERILIZATION)
94+
implementation(androidLib.KTOR_CLIENT_LOGGING)
95+
implementation(androidLib.KOTLINX_SERILIZATION)
96+
testImplementation(androidLib.KTOR_CLIENT_MOCK)
97+
androidTestImplementation(androidLib.KTOR_CLIENT_MOCK)
8698

8799
testImplementation (testLib.JUNIT)
88100
androidTestImplementation (androidLib.JUNIT_ANDROID)
89101
androidTestImplementation (androidLib.ESPRESSO_TEST)
90102
androidTestImplementation (androidLib.COMPOSE_UI_TESTING)
91103
debugImplementation (androidLib.COMPOSE_TOOLING_TESTING)
92104
debugImplementation (androidLib.COMPOSE_UI_MANIFEST_TEST)
105+
93106
}

app/src/androidTest/java/ytemplate/android/data/TestDataModule.kt

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,23 @@ import dagger.Binds
44
import dagger.Module
55
import dagger.hilt.components.SingletonComponent
66
import dagger.hilt.testing.TestInstallIn
7-
import ytemplate.android.data.di.DataModule
8-
import ytemplate.android.data.di.FakeMyModelRepository
7+
import ytemplate.android.data.datasource.LocalPostDataSource
8+
import ytemplate.android.data.datasource.LocalPostDataSourceImpl
9+
import ytemplate.android.data.datasource.RemotePostDataSource
10+
import ytemplate.android.data.datasource.RemotePostDataSourceImpl
11+
import ytemplate.android.data.di.RepositoryModule
912

1013
@Module
1114
@TestInstallIn(
1215
components = [SingletonComponent::class],
13-
replaces = [DataModule::class]
16+
replaces = [RepositoryModule::class]
1417
)
1518
interface TestDataModule {
1619
@Binds
17-
fun bindMyModelRepository(fakeMyModelRepository: FakeMyModelRepository): MyModelRepository
20+
fun bindMyModelRepository(myPostRepository: MyPostRepositoryImpl): MyPostRepository
21+
@Binds
22+
fun bindLocalPostDataSource(localPostDataSource: LocalPostDataSourceImpl): LocalPostDataSource
23+
24+
@Binds
25+
fun bindRemotePostDataSource(remotePostDataSource: RemotePostDataSourceImpl): RemotePostDataSource
1826
}

app/src/androidTest/java/ytemplate/android/database/AppDataBaseTest.kt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@ import org.junit.Assert
1212
import org.junit.Before
1313
import org.junit.Test
1414
import org.junit.runner.RunWith
15-
import ytemplate.android.database.model.MyModel
16-
import ytemplate.android.database.model.MyModelDao
15+
import ytemplate.android.database.model.Post
16+
import ytemplate.android.database.model.PostDao
1717

1818
@RunWith(AndroidJUnit4::class)
1919
class AppDataBaseTest {
20-
private lateinit var myModelDao: MyModelDao
20+
private lateinit var myModelDao: PostDao
2121
private lateinit var database: AppDataBase
2222

2323
@Before
@@ -31,10 +31,10 @@ class AppDataBaseTest {
3131
@Test
3232
@kotlin.jvm.Throws(Exception::class)
3333
fun testWriteMyModel() = runTest {
34-
val myModel = MyModel(name = "Test")
34+
val myModel = Post(1,2,"title","body")
3535
myModelDao.insert(myModel)
36-
val data = myModelDao.getMyModels().first().first()
37-
Assert.assertEquals(myModel.name, data.name)
36+
val data = myModelDao.getAllPost().first().first()
37+
Assert.assertEquals(myModel.id, data.id)
3838
}
3939

4040
@After
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package ytemplate.android.test
2+
3+
import kotlinx.coroutines.flow.Flow
4+
import kotlinx.coroutines.flow.MutableSharedFlow
5+
import ytemplate.android.database.model.Post
6+
import ytemplate.android.database.model.PostDao
7+
8+
/**
9+
* Dummy DAO class for unit testing the db operation.
10+
*/
11+
class FakePostDao : PostDao {
12+
private val data = mutableListOf<Post>()
13+
private val postFlow = MutableSharedFlow<List<Post>>()
14+
override fun getAllPost(): Flow<List<Post>> {
15+
return postFlow
16+
}
17+
18+
override suspend fun insert(post: Post) {
19+
data.add(post)
20+
}
21+
22+
override suspend fun insert(post: List<Post>) {
23+
print("Data:${post.size}")
24+
data.addAll(post)
25+
}
26+
27+
override suspend fun deleteAll() {
28+
data.clear()
29+
}
30+
31+
suspend fun emitData() {
32+
postFlow.emit(data)
33+
}
34+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package ytemplate.android.test
2+
3+
import io.ktor.client.*
4+
import io.ktor.client.engine.mock.*
5+
import io.ktor.client.plugins.contentnegotiation.*
6+
import io.ktor.client.request.*
7+
import io.ktor.http.*
8+
import io.ktor.serialization.kotlinx.json.*
9+
import kotlinx.serialization.json.Json
10+
11+
/**
12+
* RemotePostDataMock will provide dummy http client for get post api.
13+
*
14+
*/
15+
class RemotePostDataMock {
16+
17+
private var isSuccess: Boolean? = null
18+
get() = field ?: throw IllegalStateException("Response not initialized")
19+
20+
private var expectedOutput: String? = null
21+
get() = field ?: throw IllegalStateException("Response not initialized")
22+
23+
fun setExpectSuccess() {
24+
isSuccess = true
25+
}
26+
fun setExpectFailure() {
27+
isSuccess = false
28+
}
29+
30+
fun setExpectedResponse(data: String) {
31+
expectedOutput = data
32+
}
33+
34+
35+
private val mockEngine = MockEngine { req ->
36+
handleGetPostRequest(req) ?: errorResponse()
37+
}
38+
val httpClient = HttpClient{
39+
install(ContentNegotiation){
40+
json(Json {
41+
isLenient = true
42+
prettyPrint = true
43+
})
44+
}
45+
engine {
46+
mockEngine
47+
}
48+
}
49+
private fun MockRequestHandleScope.handleGetPostRequest(httpRequest: HttpRequestData): HttpResponseData? {
50+
if (httpRequest.url.encodedPath.contains("/post").not()) {
51+
return null
52+
}
53+
val statusCode = if (isSuccess == true && expectedOutput != null) {
54+
HttpStatusCode.OK
55+
} else {
56+
HttpStatusCode.InternalServerError
57+
}
58+
return respond(
59+
content = expectedOutput ?: "",
60+
status = statusCode,
61+
headers = headersOf(HttpHeaders.ContentType, "application/json")
62+
)
63+
}
64+
65+
private fun MockRequestHandleScope.errorResponse(): HttpResponseData {
66+
return respond(
67+
content = "",
68+
status = HttpStatusCode.BadRequest,
69+
headers = headersOf(HttpHeaders.ContentType, "application/json")
70+
)
71+
}
72+
}

app/src/androidTest/java/ytemplate/android/ui/mymodel/MyModelScreenKtTest.kt

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,37 @@ package ytemplate.android.ui.mymodel
33
import androidx.compose.ui.test.assertIsDisplayed
44
import androidx.compose.ui.test.junit4.createComposeRule
55
import androidx.compose.ui.test.onNodeWithTag
6-
import androidx.compose.ui.test.onNodeWithText
76
import androidx.test.ext.junit.runners.AndroidJUnit4
7+
import kotlinx.coroutines.Dispatchers
88
import org.junit.Before
99
import org.junit.Rule
1010
import org.junit.Test
1111
import org.junit.runner.RunWith
12-
import ytemplate.android.data.di.FakeMyModelRepository
12+
import ytemplate.android.data.MyPostRepositoryImpl
13+
import ytemplate.android.data.datasource.LocalPostDataSourceImpl
14+
import ytemplate.android.data.datasource.RemotePostDataSourceImpl
15+
import ytemplate.android.test.FakePostDao
16+
import ytemplate.android.test.RemotePostDataMock
1317
import ytemplate.android.ui.theme.YTemplateTheme
1418

1519
@RunWith(AndroidJUnit4::class)
1620
class MyModelScreenKtTest {
1721

1822
@get:Rule
1923
val composeRule = createComposeRule()
20-
lateinit var viewModel: MyModelViewModel
24+
lateinit var viewModel: MyPostViewModel
25+
2126
@Before
2227
fun setUp() {
23-
viewModel = MyModelViewModel(FakeMyModelRepository())
28+
val remotePostDataMock = RemotePostDataMock()
29+
val fakePostDao = FakePostDao()
30+
viewModel = MyPostViewModel(
31+
MyPostRepositoryImpl(
32+
Dispatchers.IO,
33+
LocalPostDataSourceImpl(fakePostDao),
34+
remotePostDataSource = RemotePostDataSourceImpl(httpClient = remotePostDataMock.httpClient)
35+
)
36+
)
2437
}
2538

2639
@Test
@@ -32,6 +45,5 @@ class MyModelScreenKtTest {
3245
}
3346
composeRule.onNodeWithTag("add_button").assertIsDisplayed()
3447
composeRule.onNodeWithTag("name_field").assertIsDisplayed()
35-
composeRule.onNodeWithText("test1").assertIsDisplayed()
3648
}
3749
}

app/src/main/AndroidManifest.xml

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,17 @@
22
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
33
xmlns:tools="http://schemas.android.com/tools">
44

5+
<uses-permission android:name="android.permission.INTERNET" />
6+
57
<application
6-
android:allowBackup="true"
8+
android:name="ytemplate.android.YTemplate"
9+
android:allowBackup="false"
710
android:dataExtractionRules="@xml/data_extraction_rules"
811
android:fullBackupContent="@xml/backup_rules"
912
android:icon="@mipmap/ic_launcher"
1013
android:label="@string/app_name"
1114
android:roundIcon="@mipmap/ic_launcher_round"
1215
android:supportsRtl="true"
13-
android:name="ytemplate.android.YTemplate"
1416
android:theme="@style/Theme.YTemplate"
1517
tools:targetApi="31">
1618
<activity
@@ -23,9 +25,6 @@
2325
<category android:name="android.intent.category.LAUNCHER" />
2426
</intent-filter>
2527

26-
<meta-data
27-
android:name="android.app.lib_name"
28-
android:value="" />
2928
</activity>
3029
</application>
3130

app/src/main/java/ytemplate/android/YTemplate.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ package ytemplate.android
33
import android.app.Application
44
import dagger.hilt.android.HiltAndroidApp
55

6+
/**
7+
* Application class.
8+
*/
69
@HiltAndroidApp
710
class YTemplate : Application(){
811

app/src/main/java/ytemplate/android/data/MyModelRepository.kt

Lines changed: 0 additions & 24 deletions
This file was deleted.

0 commit comments

Comments
 (0)