Android普通Junit测试和Dagger 2

11

我曾在MVP中工作,通常使用纯Junit测试我的Presenter,因为Presenter只具有业务逻辑,没有任何对Android内部的引用。

现在通过切换到Dagger 2,我意识到我在设置“TestModule”时遇到了问题。

  1. 从测试类中创建组件将无法正常工作(可能是因为“apt”在那里没有运行)
  2. 没有找到任何使用标准Junit测试的Dagger示例。我找到的每个示例都只依赖于Instrumentation测试或Roboelectric(基本上会模拟活动和其他与Android相关的内容),但这对我来说只是UI测试,我不需要那些相关内容。

仅为明确事实,我正在谈论位于app->src->test文件夹而不是app->src->androidTest的测试!

那么我做错了什么吗?还是遗漏了什么?是否有人可以解释或提供关于如何在普通单元测试中使用Dagger 2的示例呢?

4个回答

7

我不确定我的解决方案是否适用于您,但我认为没有理由不适用。首先,我创建了testInjectionComponent。

@Singleton
@Component(modules = {MockNetworkModule.class})
public interface MockInjectionComponent extends InjectionComponent {
void inject(DaggerUnitTest daggerUnitTest);
}

然后,在我的单元测试中,我在before方法中添加了注入。就像这样:

@Before
public void setUp() throws Exception {
    MockInjectionComponent mockInjectionComponent = DaggerMockInjectionComponent
            .builder()
            .mockNetworkModule(new MockNetworkModule())
            .build();
    mockInjectionComponent.inject(this);
}

然后我只需注释我的注入对象。

编辑: 不要忘记在您的app.gradle文件中添加testApt "com.google.dagger:dagger-compiler:$daggerVersion"


好的,在你的 build.gradle 文件中找到了最后一个缺失的部分,添加 testApt 'com.google.dagger:dagger-compiler:2.5' 然后编译。 - Danny Beaumont
请收下我的钱,先生 :) - Ivelius
1
你不能只写下“然后我只需注解我的注入对象。”,你必须意识到这对于试图学习Dagger/DI的人来说是一个令人恼怒的细节吧?请给我们提供代码。 - Chris Hatton
这是一个关于Dagger2的示例,它可以帮助你学习Dagger。链接:https://github.com/dracul89/dagger2example - Danny Beaumont
这个解决方法在 Kotlin 中有效吗?我正在使用 kapt 插件,并按照评论中提到的添加相应的 dagger 依赖项,包括 kaptTest,但是我无法让它正常工作。 - narko
显示剩余3条评论

3

正如被接受的答案所提到的,不要忘记添加:

对于Java

Android测试

androidTestAnnotationProcessor 'com.google.dagger:dagger-compiler:$dagger_version'

JUnit 测试

testAnnotationProcessor 'com.google.dagger:dagger-compiler:$dagger_version'

Kotlin相关

Android测试

kaptAndroidTest 'com.google.dagger:dagger-compiler:$dagger_version'

JUnit 测试

kaptTest 'com.google.dagger:dagger-compiler:$dagger_version'

2

您不需要任何匕首来测试您的Presenter。Dagger的工作是满足您的类的依赖关系(依赖注入)。

例如,您有这个Presenter:

public class MyPresenter {

    Database database;
    ApiService apiService;

    @Inject
    public MyPresenter(final Database database, final ApiService apiService) {
        this.database = database;
        this.apiService = apiService;
    }
}

Dagger将为您的Presenter提供数据库(database)api服务(apiService)对象,以供Presenter使用。当运行实际应用程序(非测试)时,这些将是具有实际功能的真实对象。
在测试Presenter时,您只想测试Presenter,其他所有内容都应该被模拟。
因此,在PresenterTest中创建Presenter时,您需要使用databaseapiService的模拟版本进行创建。
然后,您可以通过模拟对象的行为来测试Presenter与这些对象的交互。
when(database.getSomething()).thenReturn(something)
b. 验证您的演示者是否按照您的意愿使用这些对象执行操作,例如:
verify(database).saveSomething()

标准的模拟方式是使用Mockito。


感谢您的回答。这正是我目前所做的。但是,如果我无法使用dagger来交换我的模块(真实模块和测试模块),那么我将失去在项目中使用DI的主要原因之一。我更喜欢定义一个测试模块,该模块将由dagger“提供”那些模拟数据库和apiService对象。这就是DI的全部意义! - Ivelius
2
如果这是你目前正在做的事情,请继续保持。否则,请解释为什么你认为需要一个测试组件来进行单元测试,并发布一些代码 :) - fweigl
也许与代码本身关系不大,更多的是对 DI 核心理解。毫无疑问,DI 使编写可测试的代码变得更容易。在维基百科上查看 DI 的定义。如果我们在测试期间自己向 presenter 注入参数,则基本上可以视为 DI 设计模式中的“注入器”。为什么不使用框架呢?如果它们永远不会互换,那么为什么我们需要 Dagger 中的不同模块呢? - Ivelius
1
“为什么不使用框架呢?” 因为这样做会增加工作量而没有任何收益。再次强调,提供一些代码将有助于了解您为什么认为需要在单元测试中使用Dagger组件。“我们为什么需要在Dagger中使用不同的模块,如果它们永远不会互换?” 当进行Espresso(或其他仪器测试)时,将真实组件替换为测试组件非常有用,因为您无法控制类的实例创建(因为Dagger在运行时创建实例),但仍希望它们使用模拟的依赖项。 - fweigl

0

您可以通过两种方式在编译时或运行时替换真实模块为虚拟模块:第一种方式是使用Google架构示例中推荐的flavors进行编译时替换;第二种方式是创建一个抽象方法,在生产代码中注入真实依赖项,在测试代码中注入虚拟依赖项。在第二种情况下,您的测试必须继承要模拟的类并从头构建组件。


当我使用Flavors替换我的模块时,即使没有dagger,我也可以做到。正如谷歌的示例所建议的那样,它们只有不同版本的Injector类,类似于提供者。基本上,他们在没有Dagger的情况下进行DI。问题是如何使用Dagger来实现?一般而言,如果我需要用Dagger解决DI问题,为什么还需要Dagger呢? :D - Ivelius

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接