Макет поля в Android Активность в тесте Robolectric - PullRequest
0 голосов
/ 05 апреля 2019

Итак, я работаю в устаревшем коде и хочу провести модульное тестирование одного из более простых экранов, а именно входа в систему. У меня нет никакой структуры 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();
    }

хотя, возможно, и не так чисто, как хотелось бы, он выполняет свою работу без необходимости изменения моего производственного кода.

1 Ответ

0 голосов
/ 05 апреля 2019

В этом случае инъекция и автоматическое создание не будут работать. Это:

@InjectMocks
private LoginActivity activity;

переопределено в настройках:

ActivityController<LoginActivity> activityController = 
   Robolectric.buildActivity(LoginActivity.class);
activity = activityController.get();

Таким образом, вам нужно вручную установить FirebaseInstanceId переменную экземпляра этого объекта:

@Before
public void setup() {
        initMocks(this);
        when(firebaseInstanceId.getToken()).thenReturn("mockToken");
        ActivityController<LoginActivity> activityController = 
            Robolectric.buildActivity(LoginActivity.class);
        activity = activityController.get();
        activity.setFirebaseInstanceId(firebaseInstanceId);         
        activityController.create();
...