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 34 @LooperMode(LEGACY) 35 @RunWith(AndroidJUnit4.class) 36 // Robolectric requires Java9 to run API 29 and above, so use API 28 instead 37 @Config(sdk = Build.VERSION_CODES.P) 38 public final class AndroidInjectionTest { 39 40 // Most positive tests are performed in javatests/dagger/android/support/functional, but 41 // Robolectric's support for framework fragments is lacking, so we supplement those tests here: 42 public static class InjectableFragment extends Fragment { 43 String tag; 44 } 45 fakeFragmentInjector(String tag)46 private static AndroidInjector<Object> fakeFragmentInjector(String tag) { 47 return instance -> { 48 if (instance instanceof InjectableFragment) { 49 ((InjectableFragment) instance).tag = tag; 50 } 51 }; 52 } 53 54 public static class ApplicationInjectsFragment extends Application implements HasAndroidInjector { 55 @Override 56 public AndroidInjector<Object> androidInjector() { 57 return fakeFragmentInjector("injected by app"); 58 } 59 } 60 61 @Config(application = ApplicationInjectsFragment.class) 62 @Test 63 public void fragmentInjectedByApplication() { 64 Activity activity = Robolectric.setupActivity(Activity.class); 65 InjectableFragment fragment = new InjectableFragment(); 66 activity.getFragmentManager().beginTransaction().add(fragment, "tag").commit(); 67 68 AndroidInjection.inject(fragment); 69 70 assertThat(fragment.tag).isEqualTo("injected by app"); 71 } 72 73 public static class ActivityInjectsFragment extends Activity implements HasAndroidInjector { 74 @Override 75 public AndroidInjector<Object> androidInjector() { 76 return fakeFragmentInjector("injected by activity"); 77 } 78 } 79 80 @Config(application = ApplicationInjectsFragment.class) 81 @Test 82 public void fragmentInjectedByActivity() { 83 ActivityInjectsFragment activity = Robolectric.setupActivity(ActivityInjectsFragment.class); 84 InjectableFragment fragment = new InjectableFragment(); 85 activity.getFragmentManager().beginTransaction().add(fragment, "tag").commit(); 86 87 AndroidInjection.inject(fragment); 88 89 assertThat(fragment.tag).isEqualTo("injected by activity"); 90 } 91 92 public static class ParentFragmentInjectsChildFragment extends Fragment 93 implements HasAndroidInjector { 94 @Override 95 public AndroidInjector<Object> androidInjector() { 96 return fakeFragmentInjector("injected by parent fragment"); 97 } 98 } 99 100 @Config(application = ApplicationInjectsFragment.class) 101 @Test 102 public void fragmentInjectedByParentFragment() { 103 ActivityInjectsFragment activity = Robolectric.setupActivity(ActivityInjectsFragment.class); 104 ParentFragmentInjectsChildFragment parentFragment = new ParentFragmentInjectsChildFragment(); 105 InjectableFragment childFragment = new InjectableFragment(); 106 107 activity.getFragmentManager().beginTransaction().add(parentFragment, "tag").commit(); 108 parentFragment 109 .getChildFragmentManager() 110 .beginTransaction() 111 .add(childFragment, "child-tag") 112 .commit(); 113 AndroidInjection.inject(childFragment); 114 115 assertThat(childFragment.tag).isEqualTo("injected by parent fragment"); 116 } 117 118 @Test 119 public void injectActivity_applicationDoesntImplementHasAndroidInjector() { 120 Activity activity = Robolectric.setupActivity(Activity.class); 121 122 try { 123 AndroidInjection.inject(activity); 124 fail(); 125 } catch (Exception e) { 126 assertThat(e) 127 .hasMessageThat() 128 .contains("Application does not implement dagger.android.HasAndroidInjector"); 129 } 130 } 131 132 @Test 133 public void injectFragment_hasFragmentInjectorNotFound() { 134 Fragment fragment = new Fragment(); 135 Activity activity = Robolectric.setupActivity(Activity.class); 136 activity.getFragmentManager().beginTransaction().add(fragment, null).commit(); 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 Activity activity = Robolectric.setupActivity(Activity.class); 171 activity.getFragmentManager().beginTransaction().add(fragment, null).commit(); 172 173 try { 174 AndroidInjection.inject(fragment); 175 fail(); 176 } catch (Exception e) { 177 assertThat(e).hasMessageThat().contains("androidInjector() returned null"); 178 } 179 } 180 181 @Test 182 public void injectActivity_nullInput() { 183 try { 184 AndroidInjection.inject((Activity) null); 185 fail(); 186 } catch (NullPointerException e) { 187 assertThat(e).hasMessageThat().contains("activity"); 188 } 189 } 190 191 @Test 192 public void injectFragment_nullInput() { 193 try { 194 AndroidInjection.inject((Fragment) null); 195 fail(); 196 } catch (NullPointerException e) { 197 assertThat(e).hasMessageThat().contains("fragment"); 198 } 199 } 200 } 201