如何使用Mockito在单元测试中调用AppCompatActivity onCreate

时间:2018-07-04 08:22:44

标签: android unit-testing mockito

我想为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(本机方法)处       在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等其他测试框架来使此代码工作?

2 个答案:

答案 0 :(得分:1)

  

是否有任何方法可以仅使用JUnit + Mockito而不使用Roboelectric等其他测试框架来使此代码工作?

不,没有求助于框架的简便方法。真正的问题是,为什么您甚至想尝试。针对一个模拟了每个依赖关系的Activity的测试开始退化为“测试重言式”,该测试只是在不增加任何值的情况下简单地反映了Activity的逻辑。

Android中的传统观点是,由于很难对Activity,Fragment等进行单元测试,因此它们应尽可能轻巧。您需要的业务逻辑可以由可测试的Presenter或ViewModel处理。您的书或教程应涵盖此内容。

如果必须针对活动编写测试,则可以编写仪表化单元测试,其中在设备上实例化了真实的活动。有关如何操作,请参见official documentation

答案 1 :(得分:0)

我可以使用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()时调用的一些内部方法/类。更具体地说...

  1. 您看到的NullPointerException是因为您的模拟类没有AppCompatDelegate,因此它尝试创建一个。如果您使用doReturn(mock(AppCompatDelegate.class)).when(activity).getDelegate();传递了模拟的AppCompatDelegate,则可以解决此问题。
  2. 最终,SupportActivity将尝试调用ReportFragment.injectIfNeededIn(this);。只需使用mockStatic(ReportFragment.class)模拟此静态类即可模拟此调用。不要忘记将两行添加到课程的顶部:@RunWith(PowerMockRunner.class)@PrepareForTest({ ReportFragment.class })

我不会争论您是否应该嘲笑onCreate方法。大卫提出了非常有效的论据。如果您确实需要在单元测试中测试onCreate方法,那么应该可以解决问题。