java - Robolectric 测试中 Android Activity 中的模拟字段
问题描述
所以,我正在处理遗留代码,我想对更简单的屏幕之一进行单元测试,即登录。我没有任何 DI 框架。
我使用 robolectric 4.0.1 和 mockito 1.10.19
目前,由于我试图模拟的对象上的 NullPointer,我的测试失败:
测试类
@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 中的说明,但似乎没有任何效果。据我了解,getFireBaseInstanceId
调用该方法时 firebaseInstanceId 不应为空,因为它在该setup()
方法期间注入了 initMocks(this) 。
这适用于其他测试,但这些测试都没有真正结合 robolectric 和 mockito。
EDIT1:
我将 initMocks(this) 作为 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();
}
虽然可能不像我想要的那样干净,但它可以完成工作而无需更改我的生产代码。
解决方案
在这种情况下,注入和自动创建将不起作用。这个:
@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();
推荐阅读
- python - 提高逻辑迭代 Python 的速度
- php - 至少包含一个的正则表达式
- ocaml - 如何正确使用whileloop返回afterwhileloop值
- python - python请求返回javascript
- ios - 如何以这种日期格式使用 JSONDecoder / Codable?
- java - 如何从 Java 访问 Kotlin 伴随对象
- jquery - 在元素处添加标签时,Html 不读取 html 标签
- java - 使用 @ElementCollection 和 @OrderColumn 注释的嵌入列表的错误顺序
- command-line - 修复命令行输出
- python - 如何在循环内创建一个计算值的numpy数组