如何使用 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 调用的?

使用 Mockito 为 Firebase 用户身份验证设置单元测试

mockito在单元测试的使用一

Mockito单元测试

如何在服务层单元测试中模拟数据库结果?

如何断言使用 mockito 调用方法