전편 : https://velog.io/@woga1999/Clean-Code-With-Kotlin-1
예상하지 못한 시나리오에 대처하는 법에 대해 이야기 할 때는 다음 사항을 고려해야 한다.
unchecked exception
들만 있다.또한, 코틀린에서는 함수가 아무것도 반환하지 않을 때 Nothing
을 사용할 수 있다.
fun infiniteLoop(): Nothing {
while (true) {
println("Hi there!")
}
}
fun throwException(): Nothing {
throw IllegalStateException()
}
에러 핸들링 또는 상태 관리할 때 쓰기 좋은 코틀린 속 개념이 하나 더 있는데, 그건 바로
공식 문서에 따르면
"Sealed 클래스는 제한된 클래스 계층을 나타내기 위해 사용됩니다. 값은 제한된 집합의 유형 중 하나를 가질 수 있지만 다른 유형을 가질 수 없습니다. 어떤 의미에서는 열거형 값의 집합도 제한되지만 각 열거형 상수는 단일 인스턴스로만 존재하는 반면 밀폐형 클래스의 하위 클래스는 상태를 포함할 수 있는 여러 인스턴스를 가질 수 있습니다."
sealed class MovieSearchResult
data class MovieFound(val movie: Movie) : MovieSearchResult()
object MovieNotFound : MovieSearchResult()
object DatabaseOffline : MovieSearchResult()
fun displayMovieResult(movieResult: MovieSearchResult) {
when(movieResult) {
is MovieFound -> println("yey, we found the movie")
is MovieNotFound -> TODO()
}
}
val <T> T.exhaustive: T
get() = this
fun displayMovieResult(movieResult: MovieSearchResult) {
when(movieResult) {
is MovieFound -> println("yey, we found the movie")
is MovieNotFound -> TODO()
}.exhaustive
}
테스트를 클린하게 유지하자:
유연성, 유지보수성, 재사용 가능성, 가독성
을 유지하고 강화해야한다(enhanced)테스트 당 하나의 aseert을 포함해야 한다: 테스트는 1개의 fail 원인을 갖기 때문이다.
@Test
public void givenValidInputsWhenGetRatesCalledThenLiveDataObserverEmitsSuccessState() {
//given
String date = "2000-10-10";
String baseCurrency = "USD";
Single<ExchangeRateResponse> expectedResponse = Single.just(ExchangeRateResponse.EMPTY);
//when
when(mockRepository.requestExchangeRates(date, baseCurrency)).thenReturn(expectedResponse);
testSubject.getRates(date, baseCurrency);
//then
State result = testSubject.getExchangeRateData().getValue();
assertThat(result).isEqualTo(new State.Success(ExchangeRateResponse.EMPTY));
verify(mockObserver).onChanged(isA(State.Loading.class));
verify(mockObserver).onChanged(isA(State.Success.class));
verify(mockObserver, times(1)).onChanged(isA(State.Loading.class));
verify(mockObserver, times(1)).onChanged(isA(State.Success.class));
verify(mockObserver, never()).onChanged(isA(State.Failure.class));
verifyNoMoreInteractions(mockObserver);
}
테스트 코드를 작성할 때는 이 원칙을 준수하자.
잘못된 코드에 대한 주석은 추가하지 말고 본인이 코드로 설명이 될 때까지 다시 작성하자.
class Log {
/** The data. */
var data: String = ""
/** The number of data. */
var numberOfData = 0
/**
* Secondary constructor.
*/
constructor(data: String){
}
}
fun updataUserPoints(user: User, points: Int) {
try {
user.points += points
} //try
catch (e: java.lang.Exception) {
//User not found
println("user not found")
} //catch
}
<Legal Comments>
/**
Coptyright (c) <year> <copyrigth holders>
Permission is hereby granted, blah blah
*/
<Warning of consequences>
fun makeStandardDateFormant(): SimpleDateFormat? {
//SimpleDateFormat is not thread safe,
//so we need to create each instance independently.
val dateFormat = SimpleDateFormat("EEE, dd MMM yyyy HH:mm: ss z")
dateFormat.timeZone = TimeZone.getTimeZone("GMT")
return dateFormat
+)
// Check to see if the employee is eligible for full benefits
if (employee.rate.equalsIgnoreCase("hours") &&
employee.objectiveDone > 3)
if (employee.isEligibleForFullBenefits())