ViewModelProviders找不到问题

本文解析了在lifecycle-viewmodel:2.0.0版本中,原ViewModelProviders类被弃用的问题,并介绍了如何通过ViewModelProvider类及其Factory参数来正确创建ViewModel实例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在官方的例子中,有这样一段代码

model = ViewModelProviders.of(this).get(NameViewModel.class);

但在实际开发中lifecycle-viewmodel:2.0.0中并不存在,该类被弃用,

并且多了一个Factory入参,需要新的方式去创建model


        userModel=new ViewModelProvider(this,
                ViewModelProvider.AndroidViewModelFactory.getInstance(getApplication())).get(UserModel.class);

 

package com.st.base.ui; import android.content.Intent; import android.graphics.Color; import android.os.Bundle; import android.view.View; import android.view.Window; import android.view.WindowManager; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import androidx.databinding.DataBindingUtil; import androidx.databinding.ViewDataBinding; import androidx.fragment.app.FragmentActivity; import androidx.lifecycle.ViewModel; import androidx.lifecycle.ViewModelProvider; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; public abstract class BaseActivity<V extends ViewDataBinding> extends AppCompatActivity implements IBaseView { protected V binding; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); //页面接受的参数方法 initParam(); //隐藏顶部和底部栏 transparentNavBar(getWindow()); //私有的初始化Databinding和ViewModel方法 initViewDataBinding(savedInstanceState); //初始化ui initView(); //私有的ViewModel与View的契约事件回调逻辑 registorUIChangeLiveDataCallBack(); //页面数据初始化方法 initData(); //页面事件监听的方法,一般用于ViewModel层转到View层的事件注册 initViewObservable(); //页面监听回调 initCallback(); } protected abstract void initView(); protected abstract void initCallback(); protected abstract void releaseCallback(); private void initViewDataBinding(Bundle savedInstanceState) { //DataBindingUtil类需要在project的build中配置 dataBinding {enabled true }, 同步后会自动关联android.databinding包 binding = DataBindingUtil.setContentView(this, initContentView(savedInstanceState)); //支持LiveData绑定xml,数据改变,UI自动会更新 binding.setLifecycleOwner(this); initViewDataModel(); } protected void initViewDataModel() { } /** * 初始化根布局 * * @return 布局layout的id */ public abstract int initContentView(Bundle savedInstanceState); protected int initVariableId() { return 0; } protected void registorUIChangeLiveDataCallBack() { } @Override public void initParam() { } @Override public ViewModel initViewModel() { return null; } @Override public void initData() { } @Override public void initViewObservable() { } /** * 跳转页面 * * @param clz 所跳转的目的Activity类 * @param bundle 跳转所携带的信息 */ public void startActivity(Class<?> clz, Bundle bundle) { Intent intent = new Intent(this, clz); if (bundle != null) { intent.putExtras(bundle); } startActivity(intent); } /** * 创建ViewModel * * @param cls * @param <T> * @return */ public <T extends ViewModel> T createViewModel(FragmentActivity activity, Class<T> cls) { return new ViewModelProvider(activity).get(cls); } @Override protected void onDestroy() { super.onDestroy(); if (binding != null) { binding.unbind(); } releaseCallback(); } protected void transparentNavBar(@NonNull final Window window) { window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION); window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); //状态栏、导航栏透明 window.setStatusBarColor(Color.TRANSPARENT); window.setNavigationBarContrastEnforced(false); window.setNavigationBarColor(Color.TRANSPARENT); View decorView = window.getDecorView(); int vis = decorView.getSystemUiVisibility(); int option = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE; decorView.setSystemUiVisibility(vis | option); } } --------- 这个代码的单元测试可以通过PowerMock和mockito实现吗
最新发布
07-25
<think>我们使用PowerMock和Mockito来为BaseActivity编写单元测试。由于BaseActivity通常包含以下特性: 1. 使用DataBinding 2. 使用ViewModel(通过ViewModelProvider或自定义的ViewModelFactory) 3. 可能包含抽象方法或需要模拟的Android环境 因此,我们需要解决以下问题: - 模拟Android环境(如Context、Activity等) - 模拟DataBinding - 模拟ViewModel的创建 - 可能还需要模拟一些静态方法(例如ViewModelProvider的静态方法) 步骤: 1. 配置测试依赖 2. 使用@RunWith(PowerMockRunner.class)和@PrepareForTest注解 3. 使用Mockito和PowerMock模拟对象 4. 编写测试用例 注意:由于PowerMock和Mockito的版本兼容性问题,请确保使用兼容版本。以下示例基于: testImplementation 'org.mockito:mockito-core:3.12.4' testImplementation 'org.powermock:powermock-api-mockito2:2.0.9' testImplementation 'org.powermock:powermock-module-junit4:2.0.9' 示例:测试一个BaseActivity的子类(例如MainActivity) 假设BaseActivity如下(简化版): ``` public abstract class BaseActivity<B extends ViewDataBinding, V extends ViewModel> extends AppCompatActivity { protected B binding; protected V viewModel; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = DataBindingUtil.setContentView(this, getLayoutId()); viewModel = ViewModelProviders.of(this).get(getViewModelClass()); // 其他初始化 } protected abstract int getLayoutId(); protected abstract Class<V> getViewModelClass(); } ``` 我们要测试MainActivity(继承自BaseActivity)中的某个方法,比如onCreate()中调用的初始化方法。 测试目标:验证在onCreate()中,ViewModel被正确初始化,并且DataBinding被设置。 由于涉及静态方法(ViewModelProviders.of),我们需要使用PowerMock来模拟静态方法。 测试类结构: ``` // 使用PowerMockRunner,并准备需要模拟的静态类 @RunWith(PowerMockRunner.class) @PrepareForTest({ViewModelProviders.class, DataBindingUtil.class}) public class MainActivityTest { @Mock private MainViewModel mockViewModel; @Mock private ActivityMainBinding mockBinding; @Mock private ViewModelProvider mockViewModelProvider; // 使用@Rule来初始化Mockito的mock @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); private MainActivity activity; @Before public void setUp() throws Exception { // 模拟静态方法 PowerMockito.mockStatic(ViewModelProviders.class); PowerMockito.mockStatic(DataBindingUtil.class); // 当调用DataBindingUtil.setContentView时返回mockBinding when(DataBindingUtil.setContentView(any(Activity.class), anyInt())).thenReturn(mockBinding); // 模拟ViewModelProviders.of方法 when(ViewModelProviders.of(any(FragmentActivity.class))).thenReturn(mockViewModelProvider); when(mockViewModelProvider.get(any(Class.class))).thenReturn(mockViewModel); // 创建MainActivity,注意:这里不能直接new,因为需要Activity的上下文,我们使用反射 activity = PowerMockito.spy(new MainActivity()); // 调用onCreate方法 activity.onCreate(null); } @Test public void testOnCreate() { // 验证DataBindingUtil.setContentView被调用 PowerMockito.verifyStatic(DataBindingUtil.class, times(1)); DataBindingUtil.setContentView(any(Activity.class), eq(R.layout.activity_main)); // 验证ViewModelProviders.of被调用 PowerMockito.verifyStatic(ViewModelProviders.class, times(1)); ViewModelProviders.of(activity); // 验证ViewModelProvider.get被调用 verify(mockViewModelProvider).get(MainViewModel.class); // 验证activity中的viewModel和binding被正确赋值 assertThat(activity.binding, is(mockBinding)); assertThat(activity.viewModel, is(mockViewModel)); } } ``` 注意: 1. 由于Activity的创建需要上下文,我们使用PowerMockito.spy来创建一个模拟的MainActivity,这样不会触发Android系统的实际初始化。 2. 我们模拟了DataBindingUtil.setContentView返回一个mock的Binding对象。 3. 我们模拟了ViewModelProviders.of方法返回一个mock的ViewModelProvider,然后模拟ViewModelProvider.get方法返回mock的ViewModel。 但是,上面的测试可能还不够,因为BaseActivity中可能有其他逻辑,比如观察LiveData。我们还需要模拟LiveData和观察者。 另外,如果BaseActivity中有抽象方法,我们需要在测试中提供一个具体的实现(比如MainActivity),或者使用mock来忽略这些抽象方法(如果不需要测试的话)。 如果BaseActivity包含抽象方法,我们可以使用一个匿名内部类来实例化BaseActivity,但这样会变得复杂。因此,我们通常测试BaseActivity的具体子类。 总结步骤: 1. 配置依赖(确保PowerMock和Mockito版本兼容) 2. 使用@RunWith(PowerMockRunner.class)和@PrepareForTest准备需要模拟的静态类 3. 使用@Mock注解创建模拟对象 4. 在setUp方法中: a. 初始化Mockito注解(通过@Rule或MockitoAnnotations.initMocks(this)) b. 模拟静态方法(使用PowerMockito.mockStatic) c. 设置静态方法的返回值 d. 创建被测试Activity的spy对象 e. 调用被测试的方法(如onCreate) 5. 编写测试方法,验证行为(使用PowerMockito.verifyStatic来验证静态方法调用,使用Mockito.verify验证模拟对象的方法调用) 注意事项: - 由于PowerMock会修改字节码,可能会与Jacoco等工具冲突,需要调整Jacoco配置(如使用离线插桩)。 - 在Android单元测试中,我们通常使用Robolectric来提供Android环境,但这里我们使用PowerMock来模拟Android类,因此不需要Robolectric。但是,如果测试中需要真实的Android组件(如视图、资源等),可能需要结合Robolectric(这样会复杂一些)。 因此,如果测试中需要Android环境,建议使用Robolectric+PowerMock+Mockito的组合,并注意兼容性。 但是,由于我们的目标是单元测试(非仪器化测试),我们尽量模拟Android依赖,而不启动真实的Android环境。 相关问题: 1. 如何模拟LiveData和观察者? 2. 如何测试BaseActivity中的抽象方法? 3. 如何结合Robolectric和PowerMock进行测试? 4. 如何解决PowerMock与Jacoco的兼容性问题?</think># 使用 PowerMock 和 Mockito 测试 Android BaseActivity 下面我将详细说明如何为包含 DataBinding 和 ViewModel 的 Android BaseActivity 编写单元测试,使用 PowerMock 和 Mockito 框架。 ## 测试环境配置 在 `build.gradle` 中添加依赖: ```groovy dependencies { testImplementation 'org.mockito:mockito-core:4.11.0' testImplementation 'org.powermock:powermock-module-junit4:2.0.9' testImplementation 'org.powermock:powermock-api-mockito2:2.0.9' testImplementation 'androidx.arch.core:core-testing:2.2.0' } ``` ## BaseActivity 示例代码 假设我们有如下 BaseActivity 结构: ```java public abstract class BaseActivity<B extends ViewDataBinding, V extends ViewModel> extends AppCompatActivity { protected B binding; protected V viewModel; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = DataBindingUtil.setContentView(this, getLayoutId()); viewModel = ViewModelProviders.of(this).get(getViewModelClass()); initViews(); observeLiveData(); } protected abstract int getLayoutId(); protected abstract Class<V> getViewModelClass(); protected abstract void initViews(); protected abstract void observeLiveData(); } ``` ## 单元测试实现 ### 1. 测试类基本结构 ```java @RunWith(PowerMockRunner.class) @PrepareForTest({ DataBindingUtil.class, ViewModelProviders.class, BaseActivity.class // 包含需要模拟的静态方法 }) @PowerMockIgnore({ "android.*", "androidx.*", "org.mockito.*", "org.robolectric.*" }) public class BaseActivityTest { @Mock private ViewDataBinding mockBinding; @Mock private ViewModel mockViewModel; @Mock private ViewModelProvider mockViewModelProvider; @Mock private FragmentActivity mockActivity; private TestActivity testActivity; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); // 模拟静态方法 PowerMockito.mockStatic(DataBindingUtil.class); PowerMockito.mockStatic(ViewModelProviders.class); // 创建测试用的具体Activity testActivity = PowerMockito.spy(new TestActivity()); } // 测试用的具体Activity实现 private static class TestActivity extends BaseActivity<ViewDataBinding, ViewModel> { @Override protected int getLayoutId() { return R.layout.test_layout; } @Override protected Class<ViewModel> getViewModelClass() { return ViewModel.class; } @Override protected void initViews() {} @Override protected void observeLiveData() {} } } ``` ### 2. 测试 onCreate 方法 ```java @Test public void testOnCreate_shouldInitializeBindingAndViewModel() { // 模拟静态方法调用 when(DataBindingUtil.setContentView(any(Activity.class), anyInt())) .thenReturn(mockBinding); when(ViewModelProviders.of(any(FragmentActivity.class))) .thenReturn(mockViewModelProvider); when(mockViewModelProvider.get(any(Class.class))) .thenReturn(mockViewModel); // 调用被测方法 testActivity.onCreate(null); // 验证绑定是否正确初始化 verify(DataBindingUtil).setContentView(eq(testActivity), eq(R.layout.test_layout)); assertNotNull(testActivity.binding); // 验证ViewModel是否正确初始化 verify(ViewModelProviders).of(eq(testActivity)); verify(mockViewModelProvider).get(eq(ViewModel.class)); assertNotNull(testActivity.viewModel); } ``` ### 3. 测试抽象方法实现 ```java @Test public void testAbstractMethods_shouldBeCalled() { // 准备模拟环境 when(DataBindingUtil.setContentView(any(), anyInt())).thenReturn(mockBinding); when(ViewModelProviders.of(any())).thenReturn(mockViewModelProvider); when(mockViewModelProvider.get(any())).thenReturn(mockViewModel); // 验证抽象方法是否被调用 testActivity.onCreate(null); // 验证initViews方法被调用 verify(testActivity).initViews(); // 验证observeLiveData方法被调用 verify(testActivity).observeLiveData(); } ``` ### 4. 测试 ViewModel 的 LiveData 观察 ```java @Test public void testLiveDataObservation_shouldUpdateUI() { // 创建测试用的LiveData MutableLiveData<String> testLiveData = new MutableLiveData<>(); // 准备模拟环境 when(mockViewModelProvider.get(any())).thenReturn(mockViewModel); when(mockViewModel.getTitle()).thenReturn(testLiveData); // 调用被测方法 testActivity.onCreate(null); // 更新LiveData值 testLiveData.postValue("New Title"); // 验证UI更新 verify(testActivity.binding).setVariable(eq(BR.title), eq("New Title")); } ``` ## 关键测试技巧 1. **静态方法模拟**: - 使用 `@PrepareForTest` 注解声明需要模拟的静态类 - 使用 `PowerMockito.mockStatic()` 创建静态方法的模拟 - 使用 `when().thenReturn()` 定义静态方法的行为 2. **抽象方法测试**: - 创建具体的测试子类实现抽象方法 - 使用 `PowerMockito.spy()` 创建部分模拟对象 - 使用 `verify()` 验证抽象方法是否被调用 3. **LiveData 测试**: - 使用 `MutableLiveData` 作为测试数据源 - 使用 `postValue()` 触发数据更新 - 验证 DataBinding 的变量设置是否正确 4. **Android 依赖模拟**: - 使用 `@PowerMockIgnore` 排除 Android 相关包 - 模拟 Context 和 Activity 相关依赖 - 避免真实 Android 框架的依赖 ## 常见问题解决 1. **PowerMock 初始化错误**: ```java @Rule public PowerMockRule rule = new PowerMockRule(); ``` 在测试类中添加此规则解决初始化问题[^1] 2. **final 类模拟问题**: ```java @PrepareForTest({FinalClass.class}) ``` 明确声明需要模拟的 final 类 3. **静态块初始化问题**: ```java @SuppressStaticInitializationFor("com.example.SomeClass") ``` 禁止静态初始化块执行 4. **Jacoco 与 PowerMock 兼容性**: - 使用 Jacoco 0.8.7+ 版本 - 在 Jacoco 配置中添加排除规则: ```groovy excludes = ['**/*$MockitoMock$*', '**/*$PowerMockito*'] ``` ## 相关问题 1. 如何在多模块项目中统一配置 PowerMock 和 Mockito?[^2] 2. 如何测试 BaseActivity 中的生命周期方法(如 onResume)? 3. 如何处理 BaseActivity 中的权限请求逻辑测试? 4. PowerMock 与 Kotlin 协程测试如何结合使用? 5. 如何验证 BaseActivity 中的导航逻辑? [^1]: 单元测试中,使用 Mockito 可以模拟视图接口和其它注入组件,验证方法调用是否符合预期 [^^2]: 测试中通过 Mockito 的 `doReturn().when()` 语法指定 mock 对象行为,使用 `verify()` 检查方法调用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值