#66 - bridge.get_ranked_posts API 연동 구현: ReadRankedPostsUseCase 클래스 추가
시작하며...
bridge.get_ranked_posts API로 포스트 리스트를 구하는 기능을 구현 중에 있구요. 저번 포스트에서는 Repository 패턴에 이를 구현하였습니다. Use Case 패턴에도 적용하는 작업이 남았는데요. 이번 포스트에서 이를 구현하고자 합니다.
작업 개요
- ReadRankedPostsUseCase 클래스 추가
- 유닛 테스트를 하기 위한 ReadRankedPostsUseCaseTest 클래스 추가
ReadRankedPostsUseCase 클래스 추가
지난 포스트에서 구현한 SteemRepository 인터페이스의 readRankedPosts 메소드를 호출하여 ranked post들을 읽어 옵니다. 코드는 아래와 같습니다.
class ReadRankedPostsUseCase(val steemRepository: SteemRepository) {
operator fun invoke(
sort: String,
tag: String,
observer: String = "",
limit: Int = 20,
lastPostItem: PostItem? = null
): Single<List<PostItem>> {
return steemRepository.readRankedPosts(
sort,
tag,
observer,
limit,
lastPostItem
)
}
}
lastPostItem 파라메터
- 포스트 리스트 처음 읽을 때: null로 대입
- 이후 추가로 포스트 더 부를 때: 포스트 리스트의 마지막 포스트 대입
observer 파라메터
- 로그인 한 경우: 사용자 계정으로 대입
- 로그인 안 한 경우: 빈 문자열 대입
코드에서 추가로 발견한 문제점
limit 파라메터의 기본값을 20으로 설정했는데, 이름 있는 상수가 아니라 리터럴(literal)로 지정했습니다. GetRankedPostParamsDTO 클래스 안에 상수를 정의했지만, ReadRankedPostsUseCase 클래스는 도메인 모듈에 속해 있습니다. 반면, GetRankedPostParamsDTO 클래스는 데이터 모듈에 속해 있죠. limit 파라메터의 기본 값을 의미하는 상수를 어디에 다시 정의해야 할지 고민해봐야 하겠습니다.
유닛 테스트를 하기 위한 ReadRankedPostsUseCaseTest 클래스 추가
이 클래스가 잘 작동하는지 테스트가 필요하죠. 유닛 테스트 코드를 만들었습니다.
class ReadRankedPostsUseCaseTest {
val readRankedPostsUseCase = ReadRankedPostsUseCase(SteemRepositoryImpl())
private fun testPostItemList(postItemList: List<PostItem>) {
for (postItem in postItemList) {
Assert.assertTrue(postItem.account.isNotEmpty())
Assert.assertTrue(postItem.permlink.isNotEmpty())
Assert.assertTrue(postItem.title.isNotEmpty())
Assert.assertTrue(postItem.content.isNotEmpty())
}
}
// Test case 1: Read the first 20 trending posts containing "kr" tag.
@Test
fun invoke_case1() {
readRankedPostsUseCase(
GetRankedPostParamsDTO.InnerParams.SORT_TRENDING,
"kr"
).subscribe { postItemList ->
Assert.assertEquals(postItemList.size, GetRankedPostParamsDTO.InnerParams.DEFAULT_LIMIT)
testPostItemList(postItemList)
}
}
// Test case 2: Read the first 20 trending posts and the second 20 ones containing "kr" tag.
@Test
fun invoke_case2() {
var lastPostItem: PostItem? = null
val postItemList = mutableListOf<PostItem>()
readRankedPostsUseCase(
GetRankedPostParamsDTO.InnerParams.SORT_TRENDING,
"kr"
).subscribe { readPostItemList ->
lastPostItem = readPostItemList.last()
postItemList.addAll(readPostItemList)
testPostItemList(postItemList)
}
readRankedPostsUseCase(
GetRankedPostParamsDTO.InnerParams.SORT_TRENDING,
"kr",
lastPostItem = lastPostItem
).subscribe { readPostItemList ->
postItemList.addAll(readPostItemList)
Assert.assertEquals(readPostItemList.size, GetRankedPostParamsDTO.InnerParams.DEFAULT_LIMIT)
Assert.assertEquals(postItemList.size, 2 * GetRankedPostParamsDTO.InnerParams.DEFAULT_LIMIT)
testPostItemList(postItemList)
}
}
}
- invoke_case1() 메소드: 태그 kr을 가진 트렌딩 포스트 20개를 정상적으로 로딩하는지 테스트
- invoke_case2() 메소드: 태그 kr을 가진 트렌딩 포스트 20개를 그리고 다시 한번 20개를 정상적으로 로딩하는지 테스트
SteemRepositoryImpl 클래스의 readRankedPosts 메소드 테스트
지난 포스트에서 SteemRepositoryImpl 클래스에 구현한 readRankedPosts 메소드가 정상적으로 실행되는지 테스트 하는 코드를 보여드리지 못 했습니다. 이번 포스트에서 추가로 보여 드립니다.
class SteemRepositoryImplTest {
// ...
@Test
fun readRankedPosts() {
steemRepository.readRankedPosts("trending", "kr").subscribe { postItemList ->
assertEquals(postItemList.size, GetRankedPostParamsDTO.InnerParams.DEFAULT_LIMIT)
for (postItem in postItemList) {
assertTrue(postItem.account.isNotEmpty())
assertTrue(postItem.permlink.isNotEmpty())
assertTrue(postItem.title.isNotEmpty())
assertTrue(postItem.content.isNotEmpty())
}
}
}
}
readRankedPosts 메소드
- SteemRepositoryImpl 클래스의 readRankedPost 메소드가 포스트 리스트를 정상적으로 불러오는지 테스트
- 테스트 조건
- 정렬 방식(sort): trending
- 태그: kr
- 포스트 개수: 20
- 테스트 대상
- 포스트의 계정이 정상적으로 로딩되었는가?
- 포스트의 permlink가 정상적으로 읽혔는가?
- 포스트 제목이 정상적인가?
- 포스트 내용에 이상 없는가?
GitHub Commit
- Added new classes ReadRankedPostsUseCase, ReadRankedPostsUseCaseTest
- Added a new method into SteemRepositoryImplTest class to test readRankedPosts method of SteemRepositoryImpl class
마치며...
어제는 마치며를 작성 못 했네요. 밤 12시 직전에 작성하다 보면 시간에 쫓기지요. 오늘은 미리 작성합니다. 확실히 이른(?) 시간에 포스팅을 하니 여유있게 글을 작성할 수 있습니다. 앞으로도 이렇게 해야지요.
태그 화면 구현하기 위해 API 연동 구현이 생각보다 많이 길어졌네요. 단계적으로 개발하기 위해 어쩔 수 없는 과정이긴 하지만, 이 글을 작성하면서도 뭔가 답답함을 저 스스로도 느끼곤 합니다. 꾸준히 하는 것만이 최선이 아닐까 합니다. 멈추지 않고 계속 전진해야죠.
지난 스팀 앱 개발기
- #65 - bridge.get_ranked_posts API 연동 구현: SteemRepository 인터페이스, SteemRepositoryImpl 클래스 수정
- #64 - bridge.get_ranked_posts API 연동 구현: SteemService 인터페이스에 메소드 추가
- #63 - bridge.get_ranked_posts API 관련 데이터 클래스 추가 수정
- #62 - bridge.get_ranked_posts API의 응답 자료를 맡을 데이터 클래스 수정
- #61 - layout_post_item.xml 수정 후 예상치 못한 빌드 오류
- #60 - bridge.get_ranked_posts API의 응답 자료를 맡을 데이터 클래스 정의
- #59 - 태그별 포스트 리스트를 구하기 위한 bridge.get_ranked_posts API
- #58 - 포스트 리스트를 구성할 항목의 레이아웃 (3) 데이터 클래스 정의 및 데이터 바인딩 적용
- #57 - 포스트 리스트를 구성할 항목의 레이아웃 (2)
- #56 - 포스트 리스트를 구성할 항목의 레이아웃
- #55 - .gitignore 파일 작성
- #54 - RxJava 관련 메모리 누수 방지 코드 작성
- #53 - 버그 수정: 인터넷 미연결시 API 실행하면 앱 강제 종료
- #52 - 인터넷 미연결시 API 실행하면 어떻게 될까?
- #51 - 파워다운 끝났으나 SP to power down 값이 0이 아닌 버그
- #50 - get_state API 쓸까? 말까?
- #49 - get_state API 시험해 보기
- #48 - 스팀 파워 값이 스팀잇 사이트와 다른 문제 분석
- #47 - 스팀 파워 값이 스팀잇 사이트와 다른 문제
- #46 - 다음 개발 아이템들
- #45 - 지갑 화면에서 다음 파워 다운 시간 보여주기
- #44 - 지갑 화면에 파워 다운 내용 보여주기
- #43 - 지갑 화면에서 VEST 값을 SP로 변환
- #42 - VEST를 SP로 변환하는 메소드
- #41 - 작성중...
- #40 - UTC 시간을 로컬 시간으로 바꾸는 코드 만들기
- #39 - condenser_api.get_dynamic_global_properties API 실행에 필요한 데이터 클래스 추가
- #38 - 지갑 화면 구현: 예금 보여주기 추가
- #37 - 지갑 화면 구현: WalletViewModel과 WalletFragment를 바인딩
- #36 - 유닛 테스트로 구현한 WalletViewModelTest 클래스에서 공통 부분 추출하여 CommonPartOfViewModelTest 클래스 만들기
- #35 - 유닛 테스트용 WalletViewModelTest 클래스 구현 후 보이는 문제점
- #34 - WalletViewModel 클래스에 스팀 지갑 정보 읽는 코드 작성
- #33 - 추가 리팩토링: 데이터 모듈의 데이터 클래스의 필드들에 nullable 적용
- #32 - 리팩토링: 데이터 클래스 이름 변경
- #31 - 도메인 모듈에서 정의하는 데이터 클래스들이 DTO 패턴을 따르는 것이 맞는가?
- #30 - 진도를 더 나가기 전에 리팩토링 고려 중
- #29 - 뷰모델, Repository 패턴 그리고 Use Case 패턴의 관계
- #28 - Use Case 패턴 적용
- #27 - Repository 패턴 적용
- #26 - condenser_api.get_accounts API로 받은 계정 자료를 SteemitWalletDTO 타입으로 변환하기
- #25 - SteemClient, SteemService 잘 작동하는지 유닛 테스트
- #24 - API 실행을 담당할 SteemService 인터페이스 정의
- #23 - condenser_api.get_accounts API 연동에 필요한 데이터 클래스 정의
- #22 - API 연동에 필요한 라이브러리 추가
- #21 - 사용자 지갑 정보를 담을 SteemitWallet 클래스 정의
- #20 - 클린 아키텍처를 위한 모듈 구성 (2)
- #19 - 클린 아키텍처를 위한 모듈 구성 (1)
- #18 - VEST로부터 STEEM POWER를 계산하는 방법
- #17 - VEST를 STEEM POWER로 변환하기 위해 필요한 get_dynamic_global_properties API
- #16 - 지갑 내용을 읽기 위해 필요한 API
- #15 - 지갑 서브화면(WalletFragment) 레이아웃 구성해 보기
- #14 - 지갑 서브화면(WalletFragment)에서 계정 인식하기
- #13 - 프로파일 서브화면(ProfileFragment)에서 계정 인식하기
- #12 - 태그 서브화면에서 태그 인식하기
- #11 - 검색 레이아웃을 메인 화면에 적용
- #10 - 태그 및 계정 검색 레이아웃 만들기
- #9 - BaseActivity 클래스 정의 그리고 MainActivity 클래스에 적용
- #8 - BaseFragment 클래스 정의 그리고 기존 프래그먼트들에 적용
- #7 - ProfileFragment 그리고 WalletFragment에 데이터 바인딩 적용
- #6 - 태그별 검색 내용을 보여줄 TagsFragment에 데이터 바인딩 적용
- #5 - GitHub에 소스 올리기
- #4 - 하단 내비게이션의 탭 관련 클래스 이름 수정
- #3 - 하단 내비게이션 바의 아이콘, 텍스트 수정
- #2 - 프로젝트 생성
- #1 - 시작하며...
Posted through the AVLE Dapp (https://avle.io)
Upvoted! Thank you for supporting witness @jswit.
[광고] STEEM 개발자 커뮤니티에 참여 하시면, 다양한 혜택을 받을 수 있습니다.