1 /* 2 * Copyright (C) 2016 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.android; 18 19 import static com.google.common.truth.Truth.assertThat; 20 import static org.junit.Assert.fail; 21 import static org.robolectric.annotation.LooperMode.Mode.LEGACY; 22 23 import android.app.Activity; 24 import android.app.Application; 25 import android.app.Fragment; 26 import android.os.Build; 27 import androidx.test.ext.junit.runners.AndroidJUnit4; 28 import org.junit.Test; 29 import org.junit.runner.RunWith; 30 import org.robolectric.Robolectric; 31 import org.robolectric.annotation.Config; 32 import org.robolectric.annotation.LooperMode; 33 import org.robolectric.util.FragmentTestUtil; 34 35 @LooperMode(LEGACY) 36 @RunWith(AndroidJUnit4.class) 37 // Robolectric requires Java9 to run API 29 and above, so use API 28 instead 38 @Config(sdk = Build.VERSION_CODES.P) 39 public final class AndroidInjectionTest { 40 41 // Most positive tests are performed in javatests/dagger/android/support/functional, but 42 // Robolectric's support for framework fragments is lacking, so we supplement those tests here: 43 public static class InjectableFragment extends Fragment { 44 String tag; 45 } 46 fakeFragmentInjector(String tag)47 private static AndroidInjector<Object> fakeFragmentInjector(String tag) { 48 return instance -> { 49 if (instance instanceof InjectableFragment) { 50 ((InjectableFragment) instance).tag = tag; 51 } 52 }; 53 } 54 55 public static class ApplicationInjectsFragment extends Application implements HasAndroidInjector { 56 @Override 57 public AndroidInjector<Object> androidInjector() { 58 return fakeFragmentInjector("injected by app"); 59 } 60 } 61 62 @Config(application = ApplicationInjectsFragment.class) 63 @Test 64 public void fragmentInjectedByApplication() { 65 Activity activity = Robolectric.setupActivity(Activity.class); 66 InjectableFragment fragment = new InjectableFragment(); 67 activity.getFragmentManager().beginTransaction().add(fragment, "tag").commit(); 68 69 AndroidInjection.inject(fragment); 70 71 assertThat(fragment.tag).isEqualTo("injected by app"); 72 } 73 74 public static class ActivityInjectsFragment extends Activity implements HasAndroidInjector { 75 @Override 76 public AndroidInjector<Object> androidInjector() { 77 return fakeFragmentInjector("injected by activity"); 78 } 79 } 80 81 @Config(application = ApplicationInjectsFragment.class) 82 @Test 83 public void fragmentInjectedByActivity() { 84 ActivityInjectsFragment activity = Robolectric.setupActivity(ActivityInjectsFragment.class); 85 InjectableFragment fragment = new InjectableFragment(); 86 activity.getFragmentManager().beginTransaction().add(fragment, "tag").commit(); 87 88 AndroidInjection.inject(fragment); 89 90 assertThat(fragment.tag).isEqualTo("injected by activity"); 91 } 92 93 public static class ParentFragmentInjectsChildFragment extends Fragment 94 implements HasAndroidInjector { 95 @Override 96 public AndroidInjector<Object> androidInjector() { 97 return fakeFragmentInjector("injected by parent fragment"); 98 } 99 } 100 101 @Config(application = ApplicationInjectsFragment.class) 102 @Test 103 public void fragmentInjectedByParentFragment() { 104 ActivityInjectsFragment activity = Robolectric.setupActivity(ActivityInjectsFragment.class); 105 ParentFragmentInjectsChildFragment parentFragment = new ParentFragmentInjectsChildFragment(); 106 InjectableFragment childFragment = new InjectableFragment(); 107 108 activity.getFragmentManager().beginTransaction().add(parentFragment, "tag").commit(); 109 parentFragment 110 .getChildFragmentManager() 111 .beginTransaction() 112 .add(childFragment, "child-tag") 113 .commit(); 114 AndroidInjection.inject(childFragment); 115 116 assertThat(childFragment.tag).isEqualTo("injected by parent fragment"); 117 } 118 119 @Test 120 public void injectActivity_applicationDoesntImplementHasAndroidInjector() { 121 Activity activity = Robolectric.setupActivity(Activity.class); 122 123 try { 124 AndroidInjection.inject(activity); 125 fail(); 126 } catch (Exception e) { 127 assertThat(e) 128 .hasMessageThat() 129 .contains("Application does not implement dagger.android.HasAndroidInjector"); 130 } 131 } 132 133 @Test 134 public void injectFragment_hasFragmentInjectorNotFound() { 135 Fragment fragment = new Fragment(); 136 FragmentTestUtil.startFragment(fragment); 137 138 try { 139 AndroidInjection.inject(fragment); 140 fail(); 141 } catch (Exception e) { 142 assertThat(e).hasMessageThat().contains("No injector was found"); 143 } 144 } 145 146 private static class ApplicationReturnsNull extends Application implements HasAndroidInjector { 147 @Override 148 public AndroidInjector<Object> androidInjector() { 149 return null; 150 } 151 } 152 153 @Test 154 @Config(application = ApplicationReturnsNull.class) 155 public void activityInjector_returnsNull() { 156 Activity activity = Robolectric.setupActivity(Activity.class); 157 158 try { 159 AndroidInjection.inject(activity); 160 fail(); 161 } catch (Exception e) { 162 assertThat(e).hasMessageThat().contains("androidInjector() returned null"); 163 } 164 } 165 166 @Test 167 @Config(application = ApplicationReturnsNull.class) 168 public void fragmentInjector_returnsNull() { 169 Fragment fragment = new Fragment(); 170 FragmentTestUtil.startFragment(fragment); 171 172 try { 173 AndroidInjection.inject(fragment); 174 fail(); 175 } catch (Exception e) { 176 assertThat(e).hasMessageThat().contains("androidInjector() returned null"); 177 } 178 } 179 180 @Test 181 public void injectActivity_nullInput() { 182 try { 183 AndroidInjection.inject((Activity) null); 184 fail(); 185 } catch (NullPointerException e) { 186 assertThat(e).hasMessageThat().contains("activity"); 187 } 188 } 189 190 @Test 191 public void injectFragment_nullInput() { 192 try { 193 AndroidInjection.inject((Fragment) null); 194 fail(); 195 } catch (NullPointerException e) { 196 assertThat(e).hasMessageThat().contains("fragment"); 197 } 198 } 199 } 200