Skip to content

Commit b4c003d

Browse files
Add unit tests for OnboardingViewModel preferences
1 parent a82cdd8 commit b4c003d

File tree

1 file changed

+223
-0
lines changed

1 file changed

+223
-0
lines changed
Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
package com.d4rk.androidtutorials.java.ui.screens.onboarding;
2+
3+
import static org.junit.Assert.assertEquals;
4+
import static org.mockito.ArgumentMatchers.anyBoolean;
5+
import static org.mockito.ArgumentMatchers.anyString;
6+
import static org.mockito.Mockito.clearInvocations;
7+
import static org.mockito.Mockito.mockStatic;
8+
import static org.mockito.Mockito.times;
9+
import static org.mockito.Mockito.verify;
10+
import static org.mockito.Mockito.when;
11+
12+
import android.content.Context;
13+
import android.content.SharedPreferences;
14+
import android.content.res.Resources;
15+
16+
import androidx.appcompat.app.AppCompatDelegate;
17+
import androidx.preference.PreferenceManager;
18+
19+
import com.d4rk.androidtutorials.java.R;
20+
21+
import org.junit.After;
22+
import org.junit.Before;
23+
import org.junit.Test;
24+
import org.junit.runner.RunWith;
25+
import org.mockito.Mock;
26+
import org.mockito.MockedStatic;
27+
import org.mockito.junit.MockitoJUnitRunner;
28+
29+
@RunWith(MockitoJUnitRunner.class)
30+
public class OnboardingViewModelTest {
31+
32+
private static final String THEME_KEY = "theme";
33+
private static final String DEFAULT_TAB_KEY = "default_tab";
34+
private static final String BOTTOM_NAV_LABELS_KEY = "labels";
35+
private static final String MONOSPACE_FONT_KEY = "monospace_font";
36+
private static final String CRASHLYTICS_KEY = "firebase_crashlytics";
37+
private static final String CONSENT_ANALYTICS_KEY = "consent_analytics_storage";
38+
private static final String CONSENT_AD_STORAGE_KEY = "consent_ad_storage";
39+
private static final String CONSENT_AD_USER_DATA_KEY = "consent_ad_user_data";
40+
private static final String CONSENT_AD_PERSONALIZATION_KEY = "consent_ad_personalization";
41+
private static final String ONBOARDING_COMPLETE_KEY = "onboarding_complete";
42+
43+
private static final String[] THEME_VALUES = new String[]{"MODE_NIGHT_FOLLOW_SYSTEM", "MODE_NIGHT_NO"};
44+
private static final String[] TAB_VALUES = new String[]{"home", "android_studio"};
45+
private static final String[] BOTTOM_NAV_LABEL_VALUES = new String[]{"labeled", "selected"};
46+
private static final String[] CODE_FONT_VALUES = new String[]{"0", "1"};
47+
48+
@Mock
49+
private Context context;
50+
51+
@Mock
52+
private Resources resources;
53+
54+
@Mock
55+
private SharedPreferences sharedPreferences;
56+
57+
@Mock
58+
private SharedPreferences.Editor editor;
59+
60+
private MockedStatic<PreferenceManager> preferenceManagerMock;
61+
private MockedStatic<AppCompatDelegate> appCompatDelegateMock;
62+
63+
@Before
64+
public void setUp() {
65+
when(context.getResources()).thenReturn(resources);
66+
67+
when(sharedPreferences.edit()).thenReturn(editor);
68+
when(editor.putString(anyString(), anyString())).thenReturn(editor);
69+
when(editor.putBoolean(anyString(), anyBoolean())).thenReturn(editor);
70+
71+
when(context.getString(R.string.key_theme)).thenReturn(THEME_KEY);
72+
when(context.getString(R.string.key_default_tab)).thenReturn(DEFAULT_TAB_KEY);
73+
when(context.getString(R.string.key_bottom_navigation_bar_labels)).thenReturn(BOTTOM_NAV_LABELS_KEY);
74+
when(context.getString(R.string.key_monospace_font)).thenReturn(MONOSPACE_FONT_KEY);
75+
when(context.getString(R.string.key_firebase_crashlytics)).thenReturn(CRASHLYTICS_KEY);
76+
when(context.getString(R.string.key_consent_analytics)).thenReturn(CONSENT_ANALYTICS_KEY);
77+
when(context.getString(R.string.key_consent_ad_storage)).thenReturn(CONSENT_AD_STORAGE_KEY);
78+
when(context.getString(R.string.key_consent_ad_user_data)).thenReturn(CONSENT_AD_USER_DATA_KEY);
79+
when(context.getString(R.string.key_consent_ad_personalization)).thenReturn(CONSENT_AD_PERSONALIZATION_KEY);
80+
when(context.getString(R.string.key_onboarding_complete)).thenReturn(ONBOARDING_COMPLETE_KEY);
81+
82+
when(resources.getStringArray(R.array.preference_theme_values)).thenReturn(THEME_VALUES);
83+
when(resources.getStringArray(R.array.preference_default_tab_values)).thenReturn(TAB_VALUES);
84+
when(resources.getStringArray(R.array.preference_bottom_navigation_bar_labels_values)).thenReturn(BOTTOM_NAV_LABEL_VALUES);
85+
when(resources.getStringArray(R.array.code_font_values)).thenReturn(CODE_FONT_VALUES);
86+
87+
preferenceManagerMock = mockStatic(PreferenceManager.class);
88+
preferenceManagerMock.when(() -> PreferenceManager.getDefaultSharedPreferences(context)).thenReturn(sharedPreferences);
89+
90+
appCompatDelegateMock = mockStatic(AppCompatDelegate.class);
91+
}
92+
93+
@After
94+
public void tearDown() {
95+
preferenceManagerMock.close();
96+
appCompatDelegateMock.close();
97+
}
98+
99+
private OnboardingViewModel createViewModelWithExistingPrefs() {
100+
when(sharedPreferences.contains(THEME_KEY)).thenReturn(true);
101+
when(sharedPreferences.contains(DEFAULT_TAB_KEY)).thenReturn(true);
102+
return new OnboardingViewModel(context);
103+
}
104+
105+
@Test
106+
public void constructor_initializesDefaults_whenPreferencesMissing() {
107+
when(sharedPreferences.contains(THEME_KEY)).thenReturn(false);
108+
when(sharedPreferences.contains(DEFAULT_TAB_KEY)).thenReturn(false);
109+
110+
new OnboardingViewModel(context);
111+
112+
appCompatDelegateMock.verify(() -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM));
113+
verify(editor).putString(THEME_KEY, THEME_VALUES[0]);
114+
verify(editor).putString(DEFAULT_TAB_KEY, TAB_VALUES[0]);
115+
verify(editor, times(2)).apply();
116+
}
117+
118+
@Test
119+
public void setTheme_persistsValue_andGetThemeReturnsValue() {
120+
OnboardingViewModel viewModel = createViewModelWithExistingPrefs();
121+
clearInvocations(editor, sharedPreferences);
122+
123+
String newTheme = THEME_VALUES[1];
124+
viewModel.setTheme(newTheme);
125+
126+
verify(editor).putString(THEME_KEY, newTheme);
127+
verify(editor).apply();
128+
129+
when(sharedPreferences.getString(THEME_KEY, THEME_VALUES[0])).thenReturn(newTheme);
130+
131+
assertEquals(newTheme, viewModel.getTheme());
132+
verify(sharedPreferences).getString(THEME_KEY, THEME_VALUES[0]);
133+
}
134+
135+
@Test
136+
public void setDefaultTab_persistsValue() {
137+
OnboardingViewModel viewModel = createViewModelWithExistingPrefs();
138+
clearInvocations(editor);
139+
140+
String newTab = TAB_VALUES[1];
141+
viewModel.setDefaultTab(newTab);
142+
143+
verify(editor).putString(DEFAULT_TAB_KEY, newTab);
144+
verify(editor).apply();
145+
}
146+
147+
@Test
148+
public void setBottomNavLabels_persistsValue_andGetBottomNavLabelsReturnsValue() {
149+
OnboardingViewModel viewModel = createViewModelWithExistingPrefs();
150+
clearInvocations(editor, sharedPreferences);
151+
152+
String newValue = BOTTOM_NAV_LABEL_VALUES[1];
153+
viewModel.setBottomNavLabels(newValue);
154+
155+
verify(editor).putString(BOTTOM_NAV_LABELS_KEY, newValue);
156+
verify(editor).apply();
157+
158+
when(sharedPreferences.getString(BOTTOM_NAV_LABELS_KEY, BOTTOM_NAV_LABEL_VALUES[0])).thenReturn(newValue);
159+
160+
assertEquals(newValue, viewModel.getBottomNavLabels());
161+
verify(sharedPreferences).getString(BOTTOM_NAV_LABELS_KEY, BOTTOM_NAV_LABEL_VALUES[0]);
162+
}
163+
164+
@Test
165+
public void setMonospaceFont_persistsValue_andGetMonospaceFontReturnsValue() {
166+
OnboardingViewModel viewModel = createViewModelWithExistingPrefs();
167+
clearInvocations(editor, sharedPreferences);
168+
169+
String newFont = CODE_FONT_VALUES[1];
170+
viewModel.setMonospaceFont(newFont);
171+
172+
verify(editor).putString(MONOSPACE_FONT_KEY, newFont);
173+
verify(editor).apply();
174+
175+
when(sharedPreferences.getString(MONOSPACE_FONT_KEY, CODE_FONT_VALUES[0])).thenReturn(newFont);
176+
177+
assertEquals(newFont, viewModel.getMonospaceFont());
178+
verify(sharedPreferences).getString(MONOSPACE_FONT_KEY, CODE_FONT_VALUES[0]);
179+
}
180+
181+
@Test
182+
public void booleanSetters_persistValues() {
183+
OnboardingViewModel viewModel = createViewModelWithExistingPrefs();
184+
clearInvocations(editor);
185+
186+
viewModel.setCrashlyticsEnabled(true);
187+
verify(editor).putBoolean(CRASHLYTICS_KEY, true);
188+
189+
viewModel.setConsentAnalytics(false);
190+
verify(editor).putBoolean(CONSENT_ANALYTICS_KEY, false);
191+
192+
viewModel.setConsentAdStorage(true);
193+
verify(editor).putBoolean(CONSENT_AD_STORAGE_KEY, true);
194+
195+
viewModel.setConsentAdUserData(false);
196+
verify(editor).putBoolean(CONSENT_AD_USER_DATA_KEY, false);
197+
198+
viewModel.setConsentAdPersonalization(true);
199+
verify(editor).putBoolean(CONSENT_AD_PERSONALIZATION_KEY, true);
200+
201+
verify(editor, times(5)).apply();
202+
}
203+
204+
@Test
205+
public void markOnboardingComplete_persistsFlag() {
206+
OnboardingViewModel viewModel = createViewModelWithExistingPrefs();
207+
clearInvocations(editor);
208+
209+
viewModel.markOnboardingComplete();
210+
211+
verify(editor).putBoolean(ONBOARDING_COMPLETE_KEY, true);
212+
verify(editor).apply();
213+
}
214+
215+
@Test
216+
public void setCurrentPage_updatesState() {
217+
OnboardingViewModel viewModel = createViewModelWithExistingPrefs();
218+
219+
viewModel.setCurrentPage(2);
220+
221+
assertEquals(2, viewModel.getCurrentPage());
222+
}
223+
}

0 commit comments

Comments
 (0)