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

부산대 Android_김주송_4주차 과제 (STEP2) #86

Open
wants to merge 62 commits into
base: jooiss
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
04c907f
Initial commit
MyStoryG Jun 15, 2024
eeebf44
docs: Update README.md
jooiss Jul 2, 2024
c032344
feat: Design search layout
jooiss Jul 2, 2024
f011ead
feat: Add place database
jooiss Jul 2, 2024
a289932
fix: Add trigger to prevent storing duplicate data
jooiss Jul 3, 2024
121176b
feat: Design search recyclerView and place recyclerView
jooiss Jul 3, 2024
05af938
feat: Set the layout to be displayed
jooiss Jul 3, 2024
166b988
docs: Update README.md
jooiss Jul 3, 2024
34627be
feat: Add search function of place list
jooiss Jul 3, 2024
ca0170c
refactor: change place list item
jooiss Jul 3, 2024
400b36f
refactor: resize search list item layout
jooiss Jul 4, 2024
cf1cfe3
feat: Add storing function of record list
jooiss Jul 5, 2024
bbf77e9
Initial commit
MyStoryG Jul 7, 2024
e0916b7
fix: Resolve conflict
jooiss Jul 8, 2024
df4cb5e
fix: Exclude kakao map dependency
jooiss Jul 8, 2024
c86d1d0
docs: Update README.md
jooiss Jul 8, 2024
3242db7
feat: Add database insert function
jooiss Jul 9, 2024
fd929f2
fix: Resolve screen-off feature
jooiss Jul 9, 2024
d50c57e
feat: Set API key value for kakao API integration
jooiss Jul 10, 2024
93f3d31
feat: Add data transfer object file
jooiss Jul 10, 2024
3b25cb4
feat: Add retrofit interface
jooiss Jul 10, 2024
0ae951c
refactor: Combine data activity and place activity
jooiss Jul 10, 2024
3d9c0a1
feat: Add retrofit object
jooiss Jul 10, 2024
55aef9e
feat: Update network security configuration
jooiss Jul 10, 2024
7381db6
feat: Add database insertion function
jooiss Jul 10, 2024
e52f182
refactor: Extract string to file
jooiss Jul 10, 2024
bf0301d
refactor: Change the search item design
jooiss Jul 10, 2024
ac3b1a5
feat: Reset the data at first
jooiss Jul 10, 2024
67f2174
feat: Change to category search
jooiss Jul 11, 2024
bb02709
docs: Update README.md
jooiss Jul 11, 2024
381d789
feat: Add kakao native app key
jooiss Jul 11, 2024
160ca07
feat: Add dependency about kakao map SDK
jooiss Jul 11, 2024
75e407a
feat: Design map layout
jooiss Jul 11, 2024
311395a
feat: Add map view function using the kakao map API
jooiss Jul 11, 2024
6ef3184
fix: Resolve file redundancy
jooiss Jul 11, 2024
ff3d529
fix: set text to be written in one line
jooiss Jul 11, 2024
f1e692d
refactor: Change DTO file name
jooiss Jul 12, 2024
4cf9b86
fix: Make API key resource not be saved
jooiss Jul 12, 2024
68f9aa8
fix: Call MapView method in time for activity
jooiss Jul 12, 2024
51f84de
fix: Change lifecycle function about kakao map API
jooiss Jul 14, 2024
36b98d5
fix: Change CategoryGroup finding method
jooiss Jul 14, 2024
17351fa
refactor: Separate the Retrofit network-related function
jooiss Jul 15, 2024
858022d
fix: Resolve conflict
jooiss Jul 15, 2024
1b66d98
docs: Update README.md
jooiss Jul 17, 2024
37bad8f
refactor: Delete unnecessary files
jooiss Jul 17, 2024
34e1c44
feat: Change search method by keyword
jooiss Jul 17, 2024
4f84a78
feat: Display search result on selecting a saved search record
jooiss Jul 17, 2024
4ef142d
feat: Display modal when clicking on place list
jooiss Jul 18, 2024
7cd48c3
feat: Show error message when onMapError
jooiss Jul 19, 2024
23e8fb5
feat: Resume from last position on restart
jooiss Jul 19, 2024
072cae8
test: Add UI test code for PlaceActivity
jooiss Jul 19, 2024
caeef42
test: Add UI test code for MapActivity
jooiss Jul 19, 2024
fa1b72e
test: Add unit test code for MapActivity
jooiss Jul 19, 2024
96d0b32
test: Add unit test code for PlaceActivity
jooiss Jul 19, 2024
32efd23
Merge branch 'jooiss' into jooiss-step2
jooiss Jul 19, 2024
262badf
fix: Update UI test code for PlaceActivity
jooiss Jul 19, 2024
b45160d
docs: Update README.md
jooiss Jul 19, 2024
ce86697
Merge branch 'jooiss' into jooiss-step2
LeeOhHyung Jul 21, 2024
e478150
refactor: Reorganize files according to data/domain/UI layer
jooiss Jul 22, 2024
324d73e
chore: Rename application file name
jooiss Jul 22, 2024
80757f5
refactor: Reorganize files according to clean architecture
jooiss Jul 22, 2024
88aa1f9
refactor: Apply DataBinding in MapActivity and PlaceActivity
jooiss Jul 22, 2024
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
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,22 @@
- 저장된 검색어 선택 시, 해당 검색어의 검색 결과 표시
- 검색 결과 목록 중 하나의 항목 선택 시, 해당 항목의 위치를 지도에 표시
- 앱 종료 시, 마지막 위치를 저장 -> 다시 앱 실행 시, 해당 위치로 포커스
- 카카오 지도 onMapError() 호출 시, 에러 화면 출력
- 카카오 지도 onMapError() 호출 시, 에러 화면 출력

### 2단계 - 테스트 코드 작성
- 기능 테스트
- MapActivity와 관련된 기능 테스트
- Intent로 알맞은 데이터 값 전달 여부 확인
- Intent로 받은 데이터가 있을 경우 BottomSheet 출력 확인
- PlaceActivity와 관련된 기능 테스트
- 검색어 저장 목록에 장소 추가 확인
- 검색어 저장 목록에 장소 삭제 확인
- 장소 목록 출력 확인
- 검색어 저장 목록 출력 확인
- UI 테스트
- MapActivity와 관련된 UI 테스트
- MapView 출력 확인
- 검색어 입력창 선택 시 화면 전환 확인
- PlaceActivity와 관련된 UI 테스트
- 키워드 검색 시 장소 목록 출력 확인
- 장소 목록 선택 시 화면 전환 확인
3 changes: 3 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ android {

buildFeatures {
viewBinding = true
dataBinding = true
buildConfig = true
}
}
Expand Down Expand Up @@ -68,6 +69,8 @@ dependencies {
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
androidTestImplementation("androidx.test:rules:1.5.0")
androidTestImplementation("androidx.test.espresso:espresso-intents:3.5.1")
androidTestImplementation("androidx.test.espresso:espresso-contrib:3.5.1")

implementation("androidx.datastore:datastore-preferences:1.0.0")
implementation("com.squareup.retrofit2:retrofit:2.11.0")
implementation("com.squareup.retrofit2:converter-gson:2.11.0")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package campus.tech.kakao.map

import androidx.test.espresso.Espresso
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.assertion.ViewAssertions
import androidx.test.espresso.intent.Intents
import androidx.test.espresso.intent.matcher.IntentMatchers
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.rule.ActivityTestRule
import campus.tech.kakao.map.ui.MapActivity
import campus.tech.kakao.map.ui.PlaceActivity
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class MapActivityUITest {

@get:Rule
val activityRule = ActivityTestRule(MapActivity::class.java)

@Test
fun 지도_정상_작동_여부_확인() {
// 1. MapView(mapView)가 표시되고 있는지 확인
Espresso.onView(ViewMatchers.withId(R.id.mapView))
.check(ViewAssertions.matches(ViewMatchers.isDisplayed()))

// 2. 지도 화면에 검색어 입력창(etSearch)이 표시되는지 확인
Espresso.onView(ViewMatchers.withId(R.id.etSearch))
.check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
}

@Test
fun 목록_선택시_화면_전환_여부_확인() {
Intents.init()

try {
// 1. 검색어 입력창(etSearch)을 선택하기
Espresso.onView(ViewMatchers.withId(R.id.etSearch))
.perform(ViewActions.click())

// 2. 다음 화면(PlaceActivity)으로의 인텐트가 시작되었는지 확인
Intents.intended(IntentMatchers.hasComponent(PlaceActivity::class.java.name))


} finally {
Intents.release()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package campus.tech.kakao.map

import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.rule.ActivityTestRule
import campus.tech.kakao.map.ui.MapActivity
import campus.tech.kakao.map.ui.ModalBottomSheet
import org.junit.Assert.*
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class MapActivityUnitTest {

@get:Rule
var activityRule = ActivityTestRule(MapActivity::class.java, true, false)

private lateinit var mapActivity: MapActivity

@Before
fun setUp() {
// 1. 전달할 데이터 담기
val intent = Intent().apply {
putExtra("name", "카카오")
putExtra("address", "경기도 성남시 분당구 백현동 532")
putExtra("latitude", "37.3952969470752")
putExtra("longitude", "127.110449292622")
}

// 2. intent 포함한 채 시작
mapActivity = activityRule.launchActivity(intent)
}

@Test
fun 인텐트_전달시_값_전달_확인() {
// 1. onMapReady()가 호출될 때까지 대기
Thread.sleep(2000) // Wait for async operations to complete

// 2. intent를 통해 받은 데이터 값 확인
assertEquals("37.3952969470752", mapActivity.latitude)
assertEquals("127.110449292622", mapActivity.longitude)
}

@Test
fun 인텐트_전달시_모달창_동작_확인() {
// 1. onMapReady()가 호출될 때까지 대기
Thread.sleep(2000) // Wait for async operations to complete

// 2. BottomSheet에 intent로 전달한 정보 제대로 로딩되는지 확인
val fragment = mapActivity.supportFragmentManager.findFragmentByTag("modalBottomSheet") as ModalBottomSheet?
assertNotNull(fragment)
assertEquals("카카오", fragment?.arguments?.getString("name"))
assertEquals("경기도 성남시 분당구 백현동 532", fragment?.arguments?.getString("address"))
}

@Test
fun 마지막_위치_데이터_저장_확인() {
// 1. sharedPreferences를 통해 마지막 위치 저장
mapActivity.saveLocation()

// 2. sharedPreferences를 통해 저장된 위치 받아오기
val sharedPreferences = mapActivity.getSharedPreferences("locationInfo", AppCompatActivity.MODE_PRIVATE)
val savedLatitude = sharedPreferences.getString("latitude", null)
val savedLongitude = sharedPreferences.getString("longitude", null)

// 3. 받아온 데이터 확인
assertEquals("37.3952969470752", savedLatitude)
assertEquals("127.110449292622", savedLongitude)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package campus.tech.kakao.map

import androidx.recyclerview.widget.RecyclerView
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.assertion.ViewAssertions
import androidx.test.espresso.contrib.RecyclerViewActions
import androidx.test.espresso.intent.Intents
import androidx.test.espresso.intent.matcher.IntentMatchers
import androidx.test.espresso.matcher.ViewMatchers.hasMinimumChildCount
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.rule.ActivityTestRule
import campus.tech.kakao.map.ui.MapActivity
import campus.tech.kakao.map.ui.PlaceActivity
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class PlaceActivityUITest {

@get:Rule
val activityRule = ActivityTestRule(PlaceActivity::class.java)

@Test
fun 장소_키워드_검색시_목록_조회_확인() {

// 1. 검색어 입력창(etSearch)에 검색어 입력
var searchQuery = "d"
onView(withId(R.id.etSearch))
.perform(ViewActions.typeText(searchQuery))

// 2. 장소 목록(rvPlaceList)에 검색 결과가 표시되는지 확인
Thread.sleep(1000)
onView(withId(R.id.rvPlaceList))
.check(ViewAssertions.matches(hasMinimumChildCount(1)))
}

// 보니까 키패드가 영어로 되어있으면 영어만 쳐져서 저게 안되는 듯?
@Test
fun 목록_선택시_화면_전환_여부_확인() {

Intents.init()

try {
// 1. 검색어 입력창(etSearch)에 검색어 입력
val searchQuery = "d"
onView(withId(R.id.etSearch))
.perform(ViewActions.typeText(searchQuery))

// 2. 일정 시간 대기
Thread.sleep(1000)

// 3. 장소 목록(rvPlaceList)의 첫 번째 항목 클릭
onView(withId(R.id.rvPlaceList))
.perform(
RecyclerViewActions.actionOnItemAtPosition<RecyclerView.ViewHolder>(
0, ViewActions.click()
)
)

// 4. 다음 화면(MapActivity)으로의 인텐트가 시작되었는지 확인
Intents.intended(IntentMatchers.hasComponent(MapActivity::class.java.name))


} finally {
Intents.release()
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package campus.tech.kakao.map

import android.os.Handler
import android.os.Looper
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import androidx.test.core.app.ActivityScenario
import androidx.test.ext.junit.runners.AndroidJUnit4
import campus.tech.kakao.map.domain.PlaceDataModel
import campus.tech.kakao.map.data.PlaceDatabaseAccess
import campus.tech.kakao.map.ui.PlaceActivity
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit

@RunWith(AndroidJUnit4::class)
class PlaceActivityTest {

private lateinit var placeActivity: PlaceActivity
private lateinit var mockSearchDatabaseAccess: PlaceDatabaseAccess
private lateinit var rvPlaceList: RecyclerView
private lateinit var rvSearchList: RecyclerView

@Before
fun setUp() {
if (Looper.myLooper() == null) {
Looper.prepare()
}

val scenario = ActivityScenario.launch(PlaceActivity::class.java)
scenario.onActivity { activity ->
placeActivity = activity
mockSearchDatabaseAccess = PlaceDatabaseAccess(activity, "Search.db")

placeActivity.searchDatabaseAccess = mockSearchDatabaseAccess

rvPlaceList = placeActivity.findViewById(R.id.rvPlaceList)
rvSearchList = placeActivity.findViewById(R.id.rvPlaceList)
}
}

@Test
fun 검색어_저장_리스트_및_데이터베이스_속_장소_추가() {
// 1. 장소와 빈 검색어 저장 리스트 준비
val place = PlaceDataModel("카카오", "경기도 성남시 분당구 백현동 532", null, "37.3952969470752", "127.110449292622")
val searchList = mutableListOf<PlaceDataModel>()

// 2. 장소를 빈 검색어 저장 리스트에 추가
val latch = CountDownLatch(1)
Handler(Looper.getMainLooper()).post {
placeActivity.addPlaceRecord(searchList, place)
latch.countDown()
}

// 3. 실행될 때까지 대기
latch.await(5, TimeUnit.SECONDS)

// 4. 장소가 검색어 저장 리스트에 포함된 것을 확인
assertTrue(searchList.contains(place))
assertTrue(mockSearchDatabaseAccess.getAllPlace().contains(place))
}

@Test
fun 검색어_저장_리스트_및_데이터베이스_속_장소_삭제() {
// 1. 장소와 그 장소를 집어 넣은 검색어 저장 리스트 준비
val place = PlaceDataModel("카카오", "경기도 성남시 분당구 백현동 532", null, "37.3952969470752", "127.110449292622")
val searchList = mutableListOf(place)

// 2. 검색어 저장 리스트에서 특정 장소 삭제
val latch = CountDownLatch(1)
Handler(Looper.getMainLooper()).post {
placeActivity.removePlaceRecord(searchList, place)
latch.countDown()
}

// 3. 실행될 때까지 대기
latch.await(5, TimeUnit.SECONDS)

// 4. 검색어 저장 리스트에 특정 장소가 제외된 것을 확인
assertFalse(searchList.contains(place))
assertFalse(mockSearchDatabaseAccess.getAllPlace().contains(place))
}

@Test
fun 장소_목록_화면_제어_확인() {
// 1. 특정 장소 목록을 가진 장소 리스트 준비
val placeList = listOf(PlaceDataModel("카카오", "경기도 성남시 분당구 백현동 532", null, "37.3952969470752", "127.110449292622"))

// 2. 장소 목록이 있을 때의 장소 목록 화면 설정(controlPlaceVisibility())
val latch = CountDownLatch(1)
Handler(Looper.getMainLooper()).post {
placeActivity.controlPlaceVisibility(placeList)
latch.countDown()
}

// 3. 실행될 때까지 대기
latch.await(5, TimeUnit.SECONDS)

// 4. 장소 목록이 보이는 지 확인
assertEquals(View.VISIBLE, rvPlaceList.visibility)
}

@Test
fun 검색어_저장_목록_화면_제어_확인() {
// 1. 특정 저장된 검색어 기록이 있는 검색 리스트 준비
val searchList = listOf(PlaceDataModel("카카오", "경기도 성남시 분당구 백현동 532", null, "37.3952969470752", "127.110449292622"))

// 2. 검색어 저장 목록이 있을 때의 검색어 목록 화면 설정(controlSearchVisibility())
val latch = CountDownLatch(1)
Handler(Looper.getMainLooper()).post {
placeActivity.controlPlaceVisibility(searchList)
latch.countDown()
}

// 3. 실행될 때까지 대기
latch.await(5, TimeUnit.SECONDS)

// 4. 검색어 목록이 보이는 지 확인
assertEquals(View.VISIBLE, rvSearchList.visibility)
}
}
6 changes: 3 additions & 3 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<uses-permission android:name="android.permission.INTERNET" />

<application
android:name=".KaKaoMapApplication"
android:name=".ui.KakaoMapApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
Expand All @@ -17,7 +17,7 @@
android:theme="@style/Theme.Map"
tools:targetApi="31">
<activity
android:name=".MapActivity"
android:name=".ui.MapActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
Expand All @@ -26,7 +26,7 @@
</intent-filter>
</activity>
<activity
android:name=".PlaceActivity"
android:name=".ui.PlaceActivity"
android:exported="true" />
</application>

Expand Down
Loading