• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Dagger Authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package dagger.hilt.android.testing;
18 
19 import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
20 import static com.google.common.truth.Truth.assertThat;
21 import static org.junit.Assert.assertThrows;
22 
23 import androidx.test.core.app.ApplicationProvider;
24 import androidx.test.ext.junit.runners.AndroidJUnit4;
25 import dagger.hilt.EntryPoint;
26 import dagger.hilt.EntryPoints;
27 import dagger.hilt.InstallIn;
28 import dagger.hilt.components.SingletonComponent;
29 import java.util.concurrent.atomic.AtomicReference;
30 import org.junit.Rule;
31 import org.junit.Test;
32 import org.junit.rules.RuleChain;
33 import org.junit.rules.TestRule;
34 import org.junit.runner.RunWith;
35 import org.junit.runners.model.Statement;
36 import org.robolectric.annotation.Config;
37 
38 @HiltAndroidTest
39 @RunWith(AndroidJUnit4.class)
40 @Config(application = HiltTestApplication.class)
41 public final class DelayComponentReadyTest {
42   private static final String EXPECTED_VALUE = "expected";
43 
44   @EntryPoint
45   @InstallIn(SingletonComponent.class)
46   public interface FooEntryPoint {
foo()47     String foo();
48   }
49 
50   // If true, verifies that HiltAndroidRule threw an IllegalStateException
51   private boolean verifyTestRuleThrew = false;
52 
53   // A test rule that wraps HiltAndroidRule to verify it throws
54   private final TestRule exceptionVerifyingRule =
55       (base, description) -> {
56         return new Statement() {
57           @Override
58           public void evaluate() throws Throwable {
59             AtomicReference<IllegalStateException> caught = new AtomicReference<>();
60             try {
61               base.evaluate();
62             } catch (IllegalStateException e) {
63               caught.set(e);
64               if (!verifyTestRuleThrew) {
65                 throw e;
66               }
67             }
68             if (verifyTestRuleThrew) {
69               IllegalStateException expected = caught.get();
70               if (expected == null) {
71                 throw new AssertionError("Did not throw expected expection");
72               }
73               assertThat(expected)
74                   .hasMessageThat()
75                   .isEqualTo("Used delayComponentReady(), but never called componentReady()");
76             }
77           }
78         };
79       };
80 
81   private final HiltAndroidRule rule = new HiltAndroidRule(this).delayComponentReady();
82 
83   @Rule public final RuleChain ruleChain = RuleChain.outerRule(exceptionVerifyingRule).around(rule);
84 
85   @BindValue String foo;
86 
87   @Test
testLateBindValue()88   public void testLateBindValue() throws Exception {
89     AtomicReference<String> fooRef = new AtomicReference<>();
90     OnComponentReadyRunner.addListener(
91         ApplicationProvider.getApplicationContext(),
92         FooEntryPoint.class,
93         entryPoint -> fooRef.set(entryPoint.foo()));
94 
95     // Test that setting the listener before the component is ready doesn't run the listener.
96     assertThat(fooRef.get()).isNull();
97 
98     foo = EXPECTED_VALUE;
99     rule.componentReady().inject();
100     assertThat(EntryPoints.get(getApplicationContext(), FooEntryPoint.class).foo())
101         .isEqualTo(EXPECTED_VALUE);
102   }
103 
104   @Test
testUnitializedBindValue_fails()105   public void testUnitializedBindValue_fails() throws Exception {
106     OnComponentReadyRunner.addListener(
107         ApplicationProvider.getApplicationContext(), FooEntryPoint.class, FooEntryPoint::foo);
108 
109     // foo not set
110     NullPointerException expected = assertThrows(NullPointerException.class, rule::componentReady);
111     // This is not the best error message, but it is equivalent to the message from a regular
112     // (non-delayed) @BindValue returning null;
113     assertThat(expected)
114         .hasMessageThat()
115         .isEqualTo("Cannot return null from a non-@Nullable @Provides method");
116   }
117 
118   @Test
testDoubleComponentReady_fails()119   public void testDoubleComponentReady_fails() throws Exception {
120     foo = EXPECTED_VALUE;
121     rule.componentReady();
122     IllegalStateException expected =
123         assertThrows(IllegalStateException.class, rule::componentReady);
124     assertThat(expected).hasMessageThat().isEqualTo("Called componentReady() multiple times");
125   }
126 
127   @Test
testMissingComponentReady_fails()128   public void testMissingComponentReady_fails() throws Exception {
129     // componentReady not called
130     foo = EXPECTED_VALUE;
131     IllegalStateException expected = assertThrows(IllegalStateException.class, rule::inject);
132     assertThat(expected)
133         .hasMessageThat()
134         .isEqualTo("Called inject() before calling componentReady()");
135   }
136 
137   @Test
testDelayComponentReadyAfterStart_fails()138   public void testDelayComponentReadyAfterStart_fails() throws Exception {
139     IllegalStateException expected =
140         assertThrows(IllegalStateException.class, rule::delayComponentReady);
141     assertThat(expected)
142         .hasMessageThat()
143         .isEqualTo("Called delayComponentReady after test execution started");
144     // Prevents failure due to never calling componentReady()
145     foo = EXPECTED_VALUE;
146     rule.componentReady();
147   }
148 
149   @Test
neverCallsComponentReady_fails()150   public void neverCallsComponentReady_fails() throws Exception {
151     // Does not call componentReady()
152     verifyTestRuleThrew = true;
153   }
154 }
155