Android Kotlin: "java.lang.NullPointerException: Parameter s
2024-01-04 02:08

Kotlin noob question..

As part of the Hyperskill Stopwatch with Productivity Timer solution, I wrote this piece of source code:

data class StopwatchViewState( val elapsed: String = "00:00" ) class StopwatchViewModel : ViewModel(){ // TODO without specifying elapsed in the constructor call, this throws a NullPointerException. Why, as it // has a default value set in its default constructor?? private val _state = MutableStateFlow(StopwatchViewState()) val state: StateFlow< StopwatchViewState> = _state.asStateFlow() // snip

When I run Hyperskill's automated tests against this code, I get this stack trace:

Exception in thread "SDK 29 Main Thread @coroutine#2" java.lang.NullPointerException: Parameter specified as non-null is null: method org.hyperskill.stopwatch.StopwatchViewState.$$robo$$org_hyperskill_stopwatch_StopwatchViewState$__constructor__, parameter elapsed at org.hyperskill.stopwatch.StopwatchViewState.__constructor__(StopwatchViewModel.kt) at org.hyperskill.stopwatch.StopwatchViewState.< init>(StopwatchViewModel.kt) at org.hyperskill.stopwatch.StopwatchViewModel.__constructor__(StopwatchViewModel.kt:16) at org.hyperskill.stopwatch.StopwatchViewModel.< init>(StopwatchViewModel.kt) at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source) ...

If I replace

private val _state = MutableStateFlow(StopwatchViewState())

with

private val _state = MutableStateFlow(StopwatchViewState("00:00"))

the same test passes.

Under what circumstances can this happen, or what am I misunderstanding here?


UPDATE 2023-12-16: Full stack trace

java.lang.AssertionError: Exception. Test failed on activity execution with java.lang.NullPointerException: Parameter specified as non-null is null: method org.hyperskill.stopwatch.StopwatchViewState.$$robo$$org_hyperskill_stopwatch_StopwatchViewState$__constructor__, parameter elapsed java.lang.NullPointerException: Parameter specified as non-null is null: method org.hyperskill.stopwatch.StopwatchViewState.$$robo$$org_hyperskill_stopwatch_StopwatchViewState$__constructor__, parameter elapsed at org.hyperskill.stopwatch.StopwatchViewState.__constructor__(StopwatchViewModel.kt) at org.hyperskill.stopwatch.StopwatchViewState.< init>(StopwatchViewModel.kt) at org.hyperskill.stopwatch.StopwatchViewModel.__constructor__(StopwatchViewModel.kt:16) at org.hyperskill.stopwatch.StopwatchViewModel.< init>(StopwatchViewModel.kt) at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source) at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source) at java.base/jdk.internal.reflect.ReflectionFactory.newInstance(Unknown Source) at java.base/java.lang.Class.newInstance(Unknown Source) at androidx.lifecycle.ViewModelProvider$NewInstanceFactory.create(ViewModelProvider.java:219) at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.java:278) at androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.java:112) at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:185) at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:150) at androidx.lifecycle.ViewModelLazy.getValue(ViewModelProvider.kt:54) at androidx.lifecycle.ViewModelLazy.getValue(ViewModelProvider.kt:41) at org.hyperskill.stopwatch.MainActivity.onCreate$lambda-0(MainActivity.kt:21) at org.hyperskill.stopwatch.MainActivity.onCreate$lambda-1(MainActivity.kt:23) at android.view.View.performClick(View.java:7140) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at org.robolectric.shadows.ShadowView$_View_$$Reflector22.performClick(Unknown Source) at org.robolectric.shadows.ShadowView.performClick(ShadowView.java:238) at android.view.View.performClick(View.java) at org.hyperskill.tests.stopwatch.internals.AbstractUnitTest.clickAndRun(AbstractUnitTest.kt:155) at org.hyperskill.tests.stopwatch.Stage2UnitTest$checkCountOneSecondAfterOneSecondOnStartButtonClick$1.invoke(Stage2UnitTest.kt:79) at org.hyperskill.tests.stopwatch.Stage2UnitTest$checkCountOneSecondAfterOneSecondOnStartButtonClick$1.invoke(Stage2UnitTest.kt:76) at org.hyperskill.tests.stopwatch.internals.AbstractUnitTest.testActivity(AbstractUnitTest.kt:84) at org.hyperskill.tests.stopwatch.internals.AbstractUnitTest.testActivity$default(AbstractUnitTest.kt:75) at org.hyperskill.tests.stopwatch.Stage2UnitTest.checkCountOneSecondAfterOneSecondOnStartButtonClick(Stage2UnitTest.kt:76) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) 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.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26) at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306) at org.robolectric.RobolectricTestRunner$HelperTestRunner$1.evaluate(RobolectricTestRunner.java:591) at org.robolectric.internal.SandboxTestRunner$2.lambda$evaluate$0(SandboxTestRunner.java:274) at org.robolectric.internal.bytecode.Sandbox.lambda$runOnMainThread$0(Sandbox.java:88) at java.base/java.util.concurrent.FutureTask.run(Unknown Source) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) at java.base/java.lang.Thread.run(Unknown Source) at org.hyperskill.tests.stopwatch.internals.AbstractUnitTest.testActivity(AbstractUnitTest.kt:86) at org.hyperskill.tests.stopwatch.internals.AbstractUnitTest.testActivity$default(AbstractUnitTest.kt:75) at org.hyperskill.tests.stopwatch.Stage2UnitTest.checkCountOneSecondAfterOneSecondOnStartButtonClick(Stage2UnitTest.kt:76) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.base/java.lang.reflect.Method.invoke(Unknown Source) 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.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26) at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306) at org.robolectric.RobolectricTestRunner$HelperTestRunner$1.evaluate(RobolectricTestRunner.java:591) at org.robolectric.internal.SandboxTestRunner$2.lambda$evaluate$0(SandboxTestRunner.java:274) at org.robolectric.internal.bytecode.Sandbox.lambda$runOnMainThread$0(Sandbox.java:88) at java.base/java.util.concurrent.FutureTask.run(Unknown Source) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) at java.base/java.lang.Thread.run(Unknown Source)

Answer 1 :

You have declared your Data class parameter as non-null, which expects non-null value all the time, in your case, instance of StopwatchViewState() may be passed as null, from test cases or else where in the code, which throws NullPointerException, if you explicitly pass value like this StopwatchViewState("00:00") you are ensuring that value is not null to the compiler, so it passes. To avoid the Exception you can use Elvis operator or secondary constructor to handle this scenario.

data class StopwatchViewState(val elapsed: String) { constructor(elapsed: String?) : this(elapsed ?: "00:00") }
other answer :

The issue youre facing seems to be related to the testing environment or some specific constraints of the Hyperskill automated tests. The provided code should work correctly in normal Kotlin conditions, and the default value in the data class constructor should prevent the NullPointerException when creating an instance without passing the elapsed parameter.

Since youve mentioned that replacing the line:

kotlinprivate val _state = MutableStateFlow(StopwatchViewState())

with:

kotlinprivate val _state = MutableStateFlow(StopwatchViewState("00:00"))

solves the issue, it indicates that the testing environment or conditions might not be handling the default parameter correctly.

To investigate further and potentially find a cleaner solution, you can try the following:

Consult Documentation or Support: Check the documentation or support resources provided by Hyperskill for information on how they handle default parameter values in their testing environment. There might be specific rules or limitations that you need to adhere to.

Review Testing Code: Review the testing code or scripts provided by Hyperskill for this particular project. Ensure that they are not inadvertently causing the issue, such as by not recognizing default parameter values.

Contact Hyperskill Support: If the issue persists and you cannot find a solution, consider reaching out to Hyperskill support. They may be able to provide insights into the testing environment and suggest a workaround or solution.

Update Testing Environment: Ensure that your development environment and testing environment match, including Kotlin and testing library versions. An outdated or incompatible testing environment might behave differently from the development environment.

Without access to the specific testing environment and configurations used by Hyperskill, its challenging to pinpoint the exact cause. If you can provide additional details, such as the full stack trace or any relevant portions of the testing code, it might help in offering more specific advice.