-
Notifications
You must be signed in to change notification settings - Fork 46
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
[둘리] 1단계 자동 DI 미션 제출합니다 #5
Merged
Merged
Changes from all commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
029b9ca
feat: Repository 추상화
hyemdooly 66591f0
feat: 공통으로 사용할 RepositoryModules 추가
hyemdooly 2472367
feat: ViewModelFactory 구현
hyemdooly 8fa65de
feat: 커스텀 ViewModelFactory 적용
hyemdooly cf756b3
rename: Module 네이밍 변경, 디렉토리 이동
hyemdooly e75d7cc
docs: 문서 수정
hyemdooly aab65d6
rename: 변수 네이밍 수정
hyemdooly 3f39cc3
feat: kotlin reflection 임포트
hyemdooly 072ac94
refactor: RepositoryModule Container로 수정 및 재사용 리팩터링
hyemdooly 6fd5134
feat: Application 생성하여 Container에 인스턴스 추가
hyemdooly 984ab03
refactor: requireNotNull 적용
hyemdooly ddfe49a
refactor: java reflection -> kotlin reflection으로 수정
hyemdooly 53b47d0
refactor: 함수 분리
hyemdooly b84e79d
refactor: 분리한 함수 적용
hyemdooly ee21ff5
rename: 필요없는 파일 삭제 및 패키지 이동
hyemdooly abb5186
test: CartActivityTest 추가
hyemdooly 42ad01c
test: ViewModelInjector test 추가
hyemdooly 254e9bc
docs: README.md 수정
hyemdooly File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,31 @@ | ||
# android-di | ||
# 0.5단계 | ||
## 기능 요구 사항 | ||
### 생성자 주입 - 수동 | ||
다음 문제점을 해결한다. | ||
|
||
- [x] DB 없이 테스트하기 어렵다. | ||
- [x] DB 객체를 교체하기 위해 또다른 객체를 만들어 바꿔줘야 한다. 즉, ViewModel에 직접적인 변경사항이 발생한다. | ||
|
||
# 1단계 | ||
## 기능 요구 사항 | ||
### 생성자 주입 - 자동 | ||
다음 문제점을 해결한다. | ||
|
||
- [x] ViewModel에서 참조하는 Repository가 정상적으로 주입되지 않는다. | ||
- [x] Repository를 참조하는 다른 객체가 생기면 주입 코드를 매번 만들어줘야 한다. | ||
- [x] ViewModel에 수동으로 주입되고 있는 의존성들을 자동으로 주입되도록 바꿔본다. | ||
- [x] 특정 ViewModel에서만이 아닌, 범용적으로 활용될 수 있는 자동 주입 로직을 작성한다. (MainViewModel, CartViewModel 모두 하나의 로직만 참조한다) | ||
- [x] 100개의 ViewModel이 생긴다고 가정했을 때, 자동 주입 로직 100개가 생기는 것이 아니다. 하나의 자동 주입 로직을 재사용할 수 있어야 한다. | ||
- [x] 장바구니에 접근할 때마다 매번 CartRepository 인스턴스를 새로 만들고 있다. | ||
- [x] 여러 번 인스턴스화할 필요 없는 객체는 최초 한 번만 인스턴스화한다. (이 단계에서는 너무 깊게 생각하지 말고 싱글 오브젝트로 구현해도 된다.) | ||
|
||
## 선택 요구 사항 | ||
- [ ] TDD로 DI 구현 | ||
- [x] Robolectric으로 기능 테스트 | ||
- [ ] ViewModel 테스트 | ||
- [ ] 모든 도메인 로직, Repository 단위 테스트 | ||
|
||
## 프로그래밍 요구 사항 | ||
사전에 주어진 테스트 코드가 모두 성공해야 한다. | ||
Annotation은 이 단계에서 활용하지 않는다. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package woowacourse.shopping.di | ||
|
||
import kotlin.reflect.KClass | ||
|
||
object Container { | ||
private val instances = mutableMapOf<KClass<*>, Any>() | ||
fun addInstance(type: KClass<*>, instance: Any) { | ||
instances[type] = instance | ||
} | ||
|
||
fun getInstance(type: KClass<*>): Any = | ||
instances[type] ?: throw NoSuchElementException("Unknown Instance") | ||
|
||
fun clear() { | ||
instances.clear() | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
app/src/main/java/woowacourse/shopping/di/ViewModelInjector.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package woowacourse.shopping.di | ||
|
||
import androidx.lifecycle.ViewModel | ||
import androidx.lifecycle.ViewModelProvider | ||
import androidx.lifecycle.viewmodel.initializer | ||
import androidx.lifecycle.viewmodel.viewModelFactory | ||
import kotlin.reflect.full.primaryConstructor | ||
import kotlin.reflect.jvm.jvmErasure | ||
|
||
object ViewModelInjector { | ||
inline fun <reified T : ViewModel> getInjectedViewModelFactory(): ViewModelProvider.Factory { | ||
val constructor = T::class.primaryConstructor | ||
requireNotNull(constructor) { "Unknown ViewModel Class ${T::class}" } | ||
|
||
val instances = constructor.parameters.map { | ||
Container.getInstance(it.type.jvmErasure) | ||
} | ||
|
||
return viewModelFactory { | ||
initializer { | ||
constructor.call(*instances.toTypedArray()) | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
20 changes: 20 additions & 0 deletions
20
app/src/main/java/woowacourse/shopping/ui/ShoppingApplication.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package woowacourse.shopping.ui | ||
|
||
import android.app.Application | ||
import woowacourse.shopping.data.CartRepository | ||
import woowacourse.shopping.data.CartRepositoryImpl | ||
import woowacourse.shopping.data.ProductRepository | ||
import woowacourse.shopping.data.ProductRepositoryImpl | ||
import woowacourse.shopping.di.Container | ||
|
||
class ShoppingApplication : Application() { | ||
override fun onCreate() { | ||
super.onCreate() | ||
inject() | ||
} | ||
|
||
private fun inject() { | ||
Container.addInstance(CartRepository::class, CartRepositoryImpl()) | ||
Container.addInstance(ProductRepository::class, ProductRepositoryImpl()) | ||
Comment on lines
+17
to
+18
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. 이제 생성되는 저장소가 추가되어도 유연하게 대응할 수 있겠네요! |
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
44 changes: 44 additions & 0 deletions
44
app/src/test/java/woowacourse/shopping/activity/CartActivityTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package woowacourse.shopping.activity | ||
|
||
import androidx.arch.core.executor.testing.InstantTaskExecutorRule | ||
import androidx.lifecycle.ViewModelProvider | ||
import com.google.common.truth.Truth.assertThat | ||
import org.junit.Rule | ||
import org.junit.Test | ||
import org.junit.runner.RunWith | ||
import org.robolectric.Robolectric | ||
import org.robolectric.RobolectricTestRunner | ||
import woowacourse.shopping.ui.cart.CartActivity | ||
import woowacourse.shopping.ui.cart.CartViewModel | ||
|
||
@RunWith(RobolectricTestRunner::class) | ||
class CartActivityTest { | ||
|
||
@get:Rule | ||
var instantTaskExecutorRule = InstantTaskExecutorRule() // AAC 컴포넌트들을 한 스레드에서 실행되도록 함 | ||
|
||
@Test | ||
fun `Activity 실행 테스트`() { | ||
// given | ||
val activity = Robolectric | ||
.buildActivity(CartActivity::class.java) | ||
.create() | ||
.get() | ||
|
||
// then | ||
assertThat(activity).isNotNull() | ||
} | ||
|
||
@Test | ||
fun `ViewModel 주입 테스트`() { | ||
// given | ||
val activity = Robolectric | ||
.buildActivity(CartActivity::class.java) | ||
.create() | ||
.get() | ||
val viewModel = ViewModelProvider(activity)[CartViewModel::class.java] | ||
|
||
// then | ||
assertThat(viewModel).isNotNull() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
...a/woowacourse/shopping/getOrAwaitValue.kt → ...urse/shopping/activity/getOrAwaitValue.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
59 changes: 59 additions & 0 deletions
59
app/src/test/java/woowacourse/shopping/di/ViewModelInjectorTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
package woowacourse.shopping.di | ||
|
||
import androidx.activity.viewModels | ||
import androidx.appcompat.app.AppCompatActivity | ||
import androidx.lifecycle.ViewModel | ||
import junit.framework.TestCase.assertEquals | ||
import junit.framework.TestCase.assertNotNull | ||
import org.junit.Before | ||
import org.junit.Test | ||
import org.junit.runner.RunWith | ||
import org.robolectric.Robolectric | ||
import org.robolectric.RobolectricTestRunner | ||
|
||
interface FakeRepository | ||
class FakeRepositoryImpl : FakeRepository | ||
class FakeViewModel( | ||
val fakeRepository: FakeRepository, | ||
) : ViewModel() | ||
|
||
class FakeActivity : AppCompatActivity() { | ||
val viewModel by viewModels<FakeViewModel> { ViewModelInjector.getInjectedViewModelFactory<FakeViewModel>() } | ||
} | ||
|
||
@RunWith(RobolectricTestRunner::class) | ||
class ViewModelInjectorTest { | ||
|
||
@Before | ||
fun setup() { | ||
Container.clear() | ||
} | ||
|
||
@Test | ||
fun `Container에서 타입에 맞는 instance를 찾아 ViewModel 의존성을 주입한다`() { | ||
// given | ||
val repository = FakeRepositoryImpl() | ||
Container.addInstance(FakeRepository::class, repository) | ||
val activity = Robolectric | ||
.buildActivity(FakeActivity::class.java) | ||
.create() | ||
.get() | ||
|
||
// when | ||
val viewModel = activity.viewModel | ||
|
||
// then | ||
assertNotNull(viewModel) | ||
assertEquals(viewModel.fakeRepository, repository) | ||
} | ||
|
||
@Test(expected = NoSuchElementException::class) | ||
fun `Container에 타입에 맞는 instance가 없으면 ViewModel 의존성 주입에 실패하여 에러가 발생한다`() { | ||
val activity = Robolectric | ||
.buildActivity(FakeActivity::class.java) | ||
.create() | ||
.get() | ||
|
||
activity.viewModel | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
domain/src/main/java/woowacourse/shopping/data/CartRepository.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package woowacourse.shopping.data | ||
|
||
import woowacourse.shopping.model.Product | ||
|
||
interface CartRepository { | ||
fun addCartProduct(product: Product) | ||
|
||
fun getAllCartProducts(): List<Product> | ||
|
||
fun deleteCartProduct(id: Int) | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
어디서나 접근할 수 있는 object 로 관리하면 instance 의 무결성을 보장할 수 없다고 생각합니다! 다음 미션에 참고해주세요!