如何使用 Mockito 在单元测试中调用 AppCompatActivity onCreate
Posted
技术标签:
【中文标题】如何使用 Mockito 在单元测试中调用 AppCompatActivity onCreate【英文标题】:How to call AppCompatActivity onCreate in unit test using Mockito 【发布时间】:2018-07-04 08:22:44 【问题描述】:我想为 android Activity 类编写一个简单的单元测试。扩展 Activity 时测试运行良好,但是扩展 AppCompatActivity 时测试失败:
java.lang.NullPointerException 在 android.support.v7.app.AppCompatDelegateImplBase.(AppCompatDelegateImplBase.java:117) 在 android.support.v7.app.AppCompatDelegateImplV9.(AppCompatDelegateImplV9.java:149) 在 android.support.v7.app.AppCompatDelegateImplV14.(AppCompatDelegateImplV14.java:56) 在 android.support.v7.app.AppCompatDelegate.create(AppCompatDelegate.java:202) 在 android.support.v7.app.AppCompatDelegate.create(AppCompatDelegate.java:183) 在 android.support.v7.app.AppCompatActivity.getDelegate(AppCompatActivity.java:518) 在 android.support.v7.app.AppCompatActivity.onCreate(AppCompatActivity.java:70) 在activity.MainActivity.onCreate(MainActivity.kt:15) 在 sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 在 sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 在 sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 在 java.lang.reflect.Method.invoke(Method.java:498) 在activity.MainActivity.onCreate(MainActivity.kt:15)
MainActivity 代码:
class MainActivity : AppCompatActivity()
public override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
finish()
使用 Mockito 测试代码:
private lateinit var mainActivitySpy: MainActivity
@Before
fun setUp()
mainActivitySpy = spy(MainActivity())
@Test
fun `Test case`()
mainActivitySpy.onCreate(mock())
verify(mainActivitySpy).finish()
我已尝试添加以下代码,但无济于事。
android
...
testOptions
unitTests.returnDefaultValues = true
有没有什么方法可以让这段代码使用唯一的 JUnit+Mockito 而没有其他测试框架,如 Roboelectric 等?
【问题讨论】:
【参考方案1】:我使用 PowerMock 让它工作。
testImplementation 'org.powermock:powermock-api-mockito:1.6.6'
testImplementation 'org.powermock:powermock-module-junit4:1.6.6'
然后在我的测试课中我有......
@RunWith(PowerMockRunner.class)
@PrepareForTest( ReportFragment.class )
public class AddEditBeaconActivityTests
@Test
public void test_onCreate()
// Mock some data
mockStatic(ReportFragment.class);
MyActivity activity = spy(new MyActivity());
doNothing().when(activity).initScreen();
doNothing().when(activity).setContentView(R.layout.layout);
doReturn(mock(AppCompatDelegate.class)).when(activity).getDelegate();
// Call the method
activity.onCreate(null);
// Verify that it worked
verify(activity, times(1)).setContentView(R.layout.layout);
verify(activity, times(1)).initScreen();
那么这就是 MyActivity.java 的样子...
public class MyActivity extends AppCompatActivity
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.layout);
initScreen();
这是有效的,因为您正在模拟调用 super.onCreate() 时调用的一些内部方法/类。更具体地说...
-
您看到的 NullPointerException 是因为您的模拟类没有 AppCompatDelegate,因此它尝试创建一个。如果您使用
doReturn(mock(AppCompatDelegate.class)).when(activity).getDelegate();
传入一个模拟的 AppCompatDelegate,则可以解决此问题。
最终,SupportActivity 将尝试调用ReportFragment.injectIfNeededIn(this);
。简单地用 mockStatic(ReportFragment.class) 模拟这个静态类将模拟这个调用。不要忘记将这两行添加到类的顶部:@RunWith(PowerMockRunner.class)
和 @PrepareForTest( ReportFragment.class )
我不会争论你是否应该嘲笑 onCreate 方法。大卫提出了一个非常有效的论点。如果您确实需要在单元测试中测试 onCreate 方法,这应该可以解决问题。
【讨论】:
【参考方案2】:有没有什么方法可以让这段代码使用唯一的 JUnit+Mockito 而没有其他测试框架,如 Roboelectric 等?
不,不借助框架就没有简单的方法可以做到这一点。真正的问题是为什么你甚至想尝试。针对模拟每个依赖项的 Activity 的测试开始退化为“测试重言式”,其中测试只是反映 Activity 的逻辑而不添加任何值。
Android 中的传统观点是尽可能保持Activity、Fragments 等轻量级,因为它们难以进行单元测试。您需要的业务逻辑可以由可测试的 Presenter 或 ViewModel 处理。你的书或教程应该涵盖这一点。
如果您必须针对某个 Activity 编写测试,您可以编写一个仪器化单元测试,其中在设备上实例化一个真实的 Activity。请参阅official documentation 了解如何执行此操作。
【讨论】:
以上是关于如何使用 Mockito 在单元测试中调用 AppCompatActivity onCreate的主要内容,如果未能解决你的问题,请参考以下文章
如何测试 Json.parser 不是用 mockito java 调用的?