Android的测试与Robolectric和匕首 [英] Android Testing with Robolectric and Dagger

查看:147
本文介绍了Android的测试与Robolectric和匕首的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想写用匕首一个Android应用程序。试图按照TDD的做法,我开始写一个测试我的第一个活动。对于写作测试我使用Robolectric,我试图让它在使用的Mockito不同的方案工作。

I am trying to write an Android application using Dagger. Trying to follow the TDD approach, I started writing a test for my First activity. For writing tests I am using Robolectric and I am trying to make it work in different scenarios using Mockito.

简单地说:

我有我想要测试使用robolectric的机器人活动。本次活动有一定的匕首,通过其提供的依赖。我设法通过重写应用程序类,并提供工具类的模拟,使这项工作。我现在需要的是能够改变在同一个单元测试文件中的实用工具类(使用的Mockito)的行为,以测试不同的方案。

I have an android activity which I want to test using robolectric. This activity has some of its dependencies provided through Dagger. I managed to make this work by overriding the Application class and providing a mock of the utility class. What I need now is to be able to change the behavior of the utility class(using Mockito) in the same unit test file to test different scenarios.

龙的故事:

开发和测试环境:Android的工作室0.8.6,摇篮0.12,匕首1.2.2,robolectric 2.3

Development and testing environment: Android Studio 0.8.6, gradle 0.12, dagger 1.2.2, robolectric 2.3

数据库应用类:

public class MyApplication extends DaggerApplication
{

@Override
protected List<Object> getAppModules() {
    List<Object> modules = new ArrayList<Object>();
    modules.add(new AppModule(this));
    return modules;
}
}

DaggerApplication类:

DaggerApplication class:

public abstract class DaggerApplication extends Application {

private ObjectGraph mObjectGraph;

@Override
public void onCreate() {
    super.onCreate();

    AndroidAppModule sharedAppModule = new AndroidAppModule(this);

    List<Object> modules = new ArrayList<Object>();
    modules.add(sharedAppModule);
    modules.addAll(getAppModules());

    mObjectGraph = ObjectGraph.create(modules.toArray());
}

protected abstract List<Object> getAppModules();

@Override
public void inject(Object object) {
    mObjectGraph.inject(object);
}

@Override
public ObjectGraph getObjectGraph() {
    return mObjectGraph;
}
}

测试应用程序:

Test Application:

public class TestMyApplication extends MyApplication{

@Override
protected List<Object> getAppModules() {
    List<Object> modules = super.getAppModules();
    modules.add(new GeneralUtilsModuleNoInternetConnection());
    return modules;
}    

public static <T> void injectMocks(T object) {
    CursuriDeSchimbApplication app = (TestCursuriDeSchimbApplication) Robolectric.application;
    app.inject(object);
}
}

的AppModule类:

AppModule class:

@Module(
    injects = {
            SplashScreenActivity.class
    },
    includes = AndroidAppModule.class
)
public class AppModule {

private Context app;

public AppModule()
{

}

public AppModule(Context app) {
    this.app = app;
}

@Provides
@Singleton
GeneralUtils provideGeneralUtils() {
    return new GeneralUtils();
}
}

测试模块类:

Test Module class:

@Module(
    includes = AppModule.class,
    injects = {SplashScreenActivityTest.class,
            SplashScreenActivity.class},
    overrides = true
)
public class GeneralUtilsModuleNoInternetConnection
{
public GeneralUtilsModuleNoInternetConnection() {
}

@Provides
@Singleton
GeneralUtils provideGeneralUtils() {

    GeneralUtils mockGeneralUtils = Mockito.mock(GeneralUtils.class);

    when(mockGeneralUtils.isInternetConnection()).thenReturn(false);

    return mockGeneralUtils;
}
}

测试类:

@RunWith(RobolectricTestRunner.class)
public class SplashScreenActivityTest
{
SplashScreenActivity activity;

@Before
public void setUp()
{
    activity = Robolectric.buildActivity(SplashScreenActivity.class).create().get();
}


@Test
public void testOnCreate_whenNoInternetConnection()
{
   <!-- Here I want GeneralUtils to return false when asking for internet connection -->
}
@Test
public void testOnCreate_whenThereIsInternetConnection()
{
   <!-- Here I want GeneralUtils to return true when asking for internet connection -->
}

}

如果您需要了解更多信息,请不要问。 总结:我想知道如何在同一个测试类不同的测试场景使用不同的测试匕​​首模块

If you need more information please do ask. To summarize: I would like to know how to use different test dagger modules in the same test class for different test scenarios.

感谢你。

推荐答案

好吧,首先, user2511882 我已经试过张贴这个问题,但问题是,如果你TestMyApplication,在那里我注入测试模块的结构,你会看到你的建议和我的previous尝试无法工作之前,你的解决方案。

Ok, first off, user2511882 I have tried your solution before posting the question but the thing is, if you look at the structure of TestMyApplication, where I inject the test module, you would see that your suggestion and my previous tries could not work.

重新考虑整个问题之后,我发现沿着我最初尝试的线条,并更有效的解决方案(据我可以看到它)的解决方案。首先,我不靠TestMyApplication类了。此外,我不得不做一些改变,所有MyApplication类,使之更考友(不改变其功能)。因此,所有MyApplication类看起来是这样的:

After rethinking the whole problem I have found a solution along the lines of my initial tries and also a more useful solution (as far as I can see it). First off, I do not rely on the TestMyApplication class anymore. Furthermore I had to do some changes to MyApplication class to make it more "test friendly" (without changing its functionality). So MyApplication class looks like this:

public class MyApplication extends DaggerApplication
{
   private List<Object> modules;
   public MyApplication() {
       modules = new ArrayList<Object>();
       modules.add(new AppModule(this));
   }

@Override
protected List<Object> getAppModules() {
    return modules;
}
}

现在我可以在两个测试模块,其中一个我设置要求通过互联网连接和一个将返回false相同的查询时返回true的行为。

Now I can create the two test modules, one in which I set the behavior to return true when asking for an internet connection and one which will return false for the same query.

现在,在我的测试类我想有以下几点:

Now, in my test class I would have the following:

@RunWith(RobolectricTestRunner.class)
public class SplashScreenActivityTest
{
    SplashScreenActivity activity;

    public void setUpNoInternet()
    {
// Now I can add the new test module to the application modules to override the real one in the application onCreate() method
        ((MyApplication)Robolectric.application).getAppModules().add(new GeneralUtilsModuleNoInternetConnection());
        activity = Robolectric.buildActivity(SplashScreenActivity.class).create().get();
    }
    public void setUpWithInternet()
    {
        ((MyApplication)Robolectric.application).getAppModules().add(new GeneralUtilsModuleWithInternetConnection());
        activity = Robolectric.buildActivity(SplashScreenActivity.class).create().get();
    }


    @Test
    public void testOnCreate_whenNoInternetConnection()
    {
        setUpNoInternet();
       <!-- Assertions -->
    }
    @Test
    public void testOnCreate_whenThereIsInternetConnection()
    {
        setUpWithInternet();
       <!-- Assertions -->
    }

}

这工作正常,并沿着我的最初测试计划的线条。但我认为还有一个更优雅的解决方案,而不是创建一个新的测试模块每一种情况。修改后的测试模块如下所示:

This works fine and is along the lines of my initial plan of testing. But I think there is a more elegant solution instead of creating a new test module for each situation. The modified test module looks like this:

@Module(
    includes = AppModule.class,
    injects = {SplashScreenActivityTest.class,
            SplashScreenActivity.class},
    overrides = true
)
public class GeneralUtilsModuleTest
{
    private  GeneralUtils mockGeneralUtils;

    public GeneralUtilsModuleTest() {
        mockGeneralUtils = Mockito.mock(GeneralUtils.class);
    }

    @Provides
    @Singleton
    GeneralUtils provideGeneralUtils() {

        return mockGeneralUtils;
    }

    public GeneralUtils getGeneralUtils()
    {
        return mockGeneralUtils;
    }

    public void setGeneralUtils(final GeneralUtils generalUtils)
    {
        this.mockGeneralUtils = generalUtils;
    }
}

使用这个类,测试类看起来是这样的:

Using this, the Test class looks like this:

    @RunWith(RobolectricTestRunner.class)
public class SplashScreenActivityTest
{
    SplashScreenActivity activity;

    private GeneralUtilsModuleTest testModule;
    private GeneralUtils generalUtils;

    @Before
    public void setUp()
    {
        testModule = new GeneralUtilsModuleTest();
        generalUtils = Mockito.mock(GeneralUtils.class);
    }

    public void setUpNoInternet()
    {
        when(generalUtils.isInternetConnection()).thenReturn(false);
        testModule.setGeneralUtils(generalUtils);
        ((MyApplication)Robolectric.application).getAppModules().add(testModule);
        activity = Robolectric.buildActivity(SplashScreenActivity.class).create().get();
    }
    public void setUpWithInternet()
    {
        when(generalUtils.isInternetConnection()).thenReturn(true);
        testModule.setGeneralUtils(generalUtils);
        (MyApplication)Robolectric.application).getAppModules().add(testModule);
        activity = Robolectric.buildActivity(SplashScreenActivity.class).create().get();
    }
    .....(Tests)....
}

感谢大家的帮助,我真的希望这个解决方案将帮助其他人实现在Android上更好的测试。

Thank you all for your help and I really hope that this solution will help others achieve better testing on Android.

这篇关于Android的测试与Robolectric和匕首的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆