前提・実現したいこと
kotlin独学2ヶ月目の初学者です。
ランダム関数を使って任意の天気情報を返すAPIを使った、簡単なお天気アプリを作成しています。
viewModelにてJSON文字列をStringに変換後APIに渡し、APIからはランダム関数を使ってJSON文字列で値がreturnされる、といったロジックのアプリです。APIはランダムに例外もスロ-してきます。
単体テスト作成で苦戦しているのでご助言いただきたいです。。
テスト観点としては以下を想定していますが、そもそもビルドエラ-してしまいます。
・APIから天気情報が正常に返却された場合に呼び出しもとの情報が正常に更新される確認
発生している問題・エラーメッセージ
kotlinx.serialization.json.internal.JsonDecodingException: Unexpected JSON token at offset 0: Expected '{, kind: CLASS' JSON input: at kotlinx.serialization.json.internal.JsonExceptionsKt.JsonDecodingException(JsonExceptions.kt:24) at kotlinx.serialization.json.internal.JsonExceptionsKt.JsonDecodingException(JsonExceptions.kt:32) at kotlinx.serialization.json.internal.JsonReader.fail(JsonReader.kt:333) at kotlinx.serialization.json.internal.StreamingJsonDecoder.beginStructure(StreamingJsonDecoder.kt:39) at jp.co.au.training.weather.GetAreaInfo$$serializer.deserialize(WeatherViewModel.kt:104) at jp.co.au.training.weather.GetAreaInfo$$serializer.deserialize(WeatherViewModel.kt:104) at kotlinx.serialization.json.internal.PolymorphicKt.decodeSerializableValuePolymorphic(Polymorphic.kt:63) at kotlinx.serialization.json.internal.StreamingJsonDecoder.decodeSerializableValue(StreamingJsonDecoder.kt:33) at kotlinx.serialization.json.Json.decodeFromString(Json.kt:85) at jp.co.au.training.weather.WeatherViewModel.getJsonWeatherInfo(WeatherViewModel.kt:116) at jp.co.au.training.weather.WeatherViewModelTest.getJsonWeatherInfo_apiReturnNormal_resultUpdateWeatherInfo(WeatherViewModelTest.kt:37) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:566) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.rules.TestWatcher$1.evaluate(TestWatcher.java:61) at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306) at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63) at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329) at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293) at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306) at org.junit.runners.ParentRunner.run(ParentRunner.java:413) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69) at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33) at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:220) at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:53)
該当のソースコード
kotlin
1class WeatherViewModelTest { 2 3 @get:Rule 4 val instantTaskExecutorRule = InstantTaskExecutorRule() 5 6 @Test 7 fun getJsonWeatherInfo_apiReturnNormal_resultUpdateWeatherInfo() { 8 9 // mockインスタンスを生成 10 val mockWeatherApi = mockk<WeatherApi>(relaxed = true) 11 12 // テスト対象のクラスのインスタンスを生成 13 val testTargetWeatherViewModel = WeatherViewModel(mockWeatherApi, SavedStateHandle()) 14 val result = testTargetWeatherViewModel.resultJsonWeather 15 16 // mockインスタンスに任意のパターンを設定 17 every { mockWeatherApi.fetchJsonWeather("tokyo") } returns "weather=rainy" 18 19 // テスト対象のメソッドを呼び出す 20 testTargetWeatherViewModel.getJsonWeatherInfo() 21 22 assertEquals(result, "weather=rainy" ) 23 24 } 25} 26
kotlin
1@HiltViewModel 2class WeatherViewModel @Inject constructor( 3 private var weatherApi: WeatherApi, 4 private val savedStateHandle: SavedStateHandle, 5) : ViewModel() { 6 7****中略***** 8 9 fun getJsonWeatherInfo() { 10 initializeData() 11 val jsonAreaInfo = AreaInfo("tokyo", "2021-06-22") 12 val stringAreaInfo = Json.encodeToString(jsonAreaInfo) 13 try { 14 resultJsonWeather = weatherApi.fetchJsonWeather(stringAreaInfo) 15 } catch (e: UnknownException) { 16 isGetWeather = true 17 } 18 19 val resultStringWeather = resultJsonWeather?.let { Json.decodeFromString<GetAreaInfo>(it) } 20 currentWeather = resultStringWeather?.weather 21 currentMinTemp = resultStringWeather?.minTemp 22 currentMaxTemp = resultStringWeather?.maxTemp 23 24 val weatherString = currentWeather ?: return 25 try { 26 currentWeatherImage = selectWeatherImage(weatherString) 27 } catch (e: SelectImageException) { 28 isSetImage = false 29 } 30 } 31
kotlin
1@Serializable 2data class AreaInfo( 3 val area: String, 4 val date: String 5) 6 7@Serializable 8data class GetAreaInfo( 9 val weather: String, 10 val maxTemp: Int, 11 val minTemp: Int, 12 val date: String 13) 14 15
試したこと
JSONのデコードに関するエラーが出ているようなので、
viewMolde内のgetJsonWeatherInfo()で「// 取得したJSON文字列からデータクラスを生成」以降をコメントアウトするとビルドが通りました。
テスト対象の関数の、ある部分のみをテストしたい場合のコーティング方法はあるのでしょうか
また、上記でテスト実行されたものの、Expected :nullとなりテストに失敗してしまいます。
実機で動作確認する分には正常に動作しているように見えるので、テストコードに不備があるのではと思っています。
補足情報(FW/ツールのバージョンなど)
・AndroidStudio4.2.1
・kotlin plugin version 202-1.5.10
回答1件
あなたの回答
tips
プレビュー