Итак, я работаю в устаревшем коде и хочу провести модульное тестирование одного из более простых экранов, а именно входа в систему.
У меня нет никакой структуры DI.
Я использую robolectric 4.0.1 и mockito 1.10.19
В настоящее время мой тест не пройден из-за NullPointer для объекта, который я пытался смоделировать:
TestClass
@RunWith(RobolectricTestRunner.class)
public class LoginActivityTest {
@Rule
public MockitoRule mockitoRule = MockitoJUnit.rule();
@Mock
FirebaseInstanceId firebaseInstanceId;
@InjectMocks
private LoginActivity activity;
@Before
public void setup() {
when(firebaseInstanceId.getToken()).thenReturn("mockToken");
ActivityController<LoginActivity> activityController = Robolectric.buildActivity(LoginActivity.class);
activity = activityController.get();
initMocks(this);
activityController.create();
}
@Test
public void checkThatLoginButtonExists() {
// does not reach this point
Button btn = (Button) activity.findViewById(R.id.button_login);
assertNotNull("Button exisits", btn);
}
}
тестируемый класс
public class LoginActivity extends FragmentActivity {
private Button mBtnLogin;
public LoginActivity() {
//default constructor
}
private FirebaseInstanceId firebaseInstanceId;
private FirebaseInstanceId getFirebaseInstanceId(){
if (firebaseInstanceId == null){
firebaseInstanceId = FirebaseInstanceId.getInstance();
}
return firebaseInstanceId;
}
private void initViews() {
...
mBtnLogin = (Button) findViewById(R.id.button_login);
...
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.login_layout);
...
initViews();
...
registerNotification();
}
private void registerNotification() {
String newFcmToken = getFirebaseInstanceId().getToken(); // fails here
...
}
}
Я пытался в основном изменить порядок инструкций в моем TestClass, основываясь на результатах поиска Google, но, похоже, ничего не работает.
Насколько я понимаю, firebaseInstanceId не должен быть нулевым, когда вызывается метод getFireBaseInstanceId
, поскольку он внедряется с помощью initMocks (this) во время метода setup()
.
Это работает для других тестов, но ни один из них на самом деле не сочетает в себе robolectric и mockito.
EDIT1:
Я положил initMocks (это) в качестве первой строки в setup ()
Я добавил немного System.out.println, чтобы увидеть, что происходит, и заметил, что конструктор LoginActivity получает вызовы несколько раз (один раз для initMocks, один раз для Robolectric.buildActivity)
EDIT2:
Я изменил настройки в своем тестовом классе, чтобы они выглядели следующим образом
private void setMyOwnMock(String fieldName, Object inClass, Object mock ){
Field declaredField;
try {
declaredField = inClass.getClass().getDeclaredField(fieldName);
declaredField.setAccessible(true);
declaredField.set(inClass, firebaseInstanceId);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
@Before
public void setup() {
ActivityController<LoginActivity> activityController = Robolectric.buildActivity(LoginActivity.class);
activity = activityController.get();
setMyOwnMock("firebaseInstanceId", activity, firebaseInstanceId);
when(firebaseInstanceId.getToken()).thenReturn("mockToken");
// System.out.println(activity.firebaseInstanceId == null);
activityController.create();
}
хотя, возможно, и не так чисто, как хотелось бы, он выполняет свою работу без необходимости изменения моего производственного кода.