-
Notifications
You must be signed in to change notification settings - Fork 2
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
test: add instrumented tests for active session #144
Open
matthiasemde
wants to merge
19
commits into
main
Choose a base branch
from
test/active-session
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Conversation
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
mipro98
reviewed
Dec 30, 2024
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.
leaving comments so far
app/src/androidTest/java/app/musikus/activesession/presentation/ActiveSessionScreenTest.kt
Outdated
Show resolved
Hide resolved
app/src/androidTest/java/app/musikus/activesession/presentation/ActiveSessionScreenTest.kt
Show resolved
Hide resolved
matthiasemde
force-pushed
the
test/active-session
branch
from
January 3, 2025 20:16
9461638
to
fcf0d74
Compare
This NotificationManager is dependency injected by dagger hilt which at the same time creates the notification channels. Thereby the whole mechanism works out of the box when used in tests.
… for active session use cases and pass timestamps through inputs instead This means that we are now actually using the timestamp emitted by the "clock" flow and basing calculations on it.
Some of the checks were moved around the use cases in order to ensure the user can not finish an empty session.
… querying it from the repository
Removed snackbar host from active session scaffold and used the main ui event "ShowSnackbar" instead.
matthiasemde
force-pushed
the
test/active-session
branch
from
January 3, 2025 20:45
fcf0d74
to
ea8648e
Compare
matthiasemde
force-pushed
the
test/active-session
branch
from
January 3, 2025 21:24
ea8648e
to
a7a1881
Compare
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Story
This PR adds tests for the active session. As you can imagine, this is kinda complicated because the active session has many dependencies like multiple services, use cases and repositories.
Modifications to the main code
NotificationManager
One thing I stumbled over at first was the fact that even the simplest test would fail with the error
Bad notification for startForeground()
. This lead me down the rabbit hole of trying to "fix" the service or somehow inject a test service. On the way I wrote a simple test for the session service, which doesn't really do much, because the service only shows a notification and has no public methods which could be tested.Back to the error in the active session test. The
Bad notification for startForeground()
didn't have anything to do with the service per se, but rather with the non-existence of the notification channels which are normally created by theMusikus : Application()
class. However, the application appears not to get instantiated for instrumented tests so I had to find a solution.The answer, as so many times with testing, is "Dependency injection". Creating a dedicated
NotificationModule
which provides theNotificationManager
while also creating the notification channels.Works like a charm 👌
TimeProvider
A potentially controversial change in this PR is the discretization of the clock in
TimeProvider
. The interface now looks like this:The interesting thing is the difference in implementation between
TimeProviderImpl
andFakeTimeProvider
:This implementation has two main advantages:
advanceTimeBy
orsetCurrentDateTime
is called.get**Duration
use cases are now more easily testable after being converted tocompute**Duration
use casesActiveSessionViewModel
Problems
A problem which I ran into, is the fact that our active session UI is updated by a clock. So when trying to advance the time usingDone 👌fakeTimeProvider.advanceTimeBy(90.seconds)
the UI didn't actually update adelay(100)
had passed. This, of course, is not a nice thing to add to a test. I think we should look into modifying theTimeProvider
to provide a clock-like flow of some kind, which we can then use in all our screens which need to update automatically. TheFakeTimeProvider
could then send the updates immediately.The "Pause" button, or more specific theDone 👌PauseActiveSessionUseCase
does not allow the user to pause the session if the session timer is still at zero. In reality this is never a problem, but for testing it was quite tricky to figure out why pressing "Pause" wasn't working. The check could probably go into another use case...Learnings
Use Cases
Important
To reduce undefined behavior and improve testability, use cases should not fetch data from the repository and do too much processing at the same time, especially if the data gets combined with state from the view model.
Here is an example which led to this conclusion:
This code can lead to a race condition... Can you spot it?
Since both
getPracticeDuration
andgetOngoingPauseDuration
first request the session state from the active session repository, a race condition occurs whenever the user presses "Pause" while the code execution is somewhere in front ofgetOngoingPauseDuration
and the clock has updated (10 Hz). In this case,currentPauseStartTimestamp
in the session state will get updated with the new clock value but the value ofnow
is still original value which leads to a negative pause duration.Adds to: #18