简体   繁体   English

Robolectric测试中的Android Activity中的模拟字段

[英]Mock field in Android Activity in Robolectric test

So, I'm working in legacy code, and I want to unit test one of the easier screens, namely the login. 所以,我正在使用遗留代码,我想对其中一个更简单的屏幕进行单元测试,即登录。 I don't have any DI framework. 我没有任何DI框架。

I use robolectric 4.0.1 and mockito 1.10.19 我使用robolectric 4.0.1和mockito 1.10.19

Currently, my test fails due to a NullPointer on an object that I tried to mock: 目前,由于我试图模拟的对象上的NullPointer,我的测试失败了:

TestClass 识别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);
    }
}

class under test 被测试的课程

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
        ...
    }
}

I've tried mainly to reorder the instructions in my TestClass, based on google findings, but nothing seems to work. 我试图主要根据谷歌的调查结果对我的TestClass中的指令进行重新排序,但似乎没有任何效果。 As far as I understand it, firebaseInstanceId should not be null when the getFireBaseInstanceId method is called since it is injected with initMocks(this) during the setup() method. 据我所知,当调用getFireBaseInstanceId方法时, getFireBaseInstanceId不应为null,因为在setup()方法期间注入了initMocks(this)。

This works for other tests, but none of those actually combine robolectric and mockito. 这适用于其他测试,但实际上没有一个组合robolectric和mockito。

EDIT1: EDIT1:
I've put the initMocks(this) as the first line in setup() 我把initMocks(this)作为setup()的第一行
I've added some System.out.println to see what happens, and I've noticed that the LoginActivity constructor gets calles multiple times(once for initMocks, once for Robolectric.buildActivity) 我添加了一些System.out.println以查看发生了什么,我注意到LoginActivity构造函数多次获取calles(一次用于initMocks,一次用于Robolectric.buildActivity)

EDIT2: I've changed my setup in my test class to be as follows 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();
    }

while maybe not as clean as I would like, it gets the job done without having to change my production code. 虽然可能不像我想的那样干净,但它可以完成工作,而无需更改我的生产代码。

The injection and auto-creation will not work in this case. 在这种情况下,注入和自动创建将不起作用。 This: 这个:

@InjectMocks
private LoginActivity activity;

is overridden in the set-up by: 在设置中被覆盖:

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

So you need to manually set the FirebaseInstanceId instance variable of that object: 因此,您需要手动设置该对象的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();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM