Java 我应该如何处理DI和测试的上下文对象?

Java 我应该如何处理DI和测试的上下文对象?,java,android,unit-testing,android-studio,android-context,Java,Android,Unit Testing,Android Studio,Android Context,我有一个创建位图对象的类 它需要访问位图资源,所以它需要访问我传递给它的构造函数的上下文,所以我像myClass(context) 理想情况下,我希望以某种方式注入依赖项。我想用一个工厂来实现这一点,这样我就可以向工厂请求对象,它会为我创建对象,但我只能从创建新窗口的活动类中获取上下文。因此,看来我必须在我主要活动的背景下继续传递 我最近问了一个类似的问题,他们告诉我根本不应该静态地调用上下文,应该始终传递上下文 这意味着如果我想测试这个类,我的测试类需要访问android框架(这意味着我不能在

我有一个创建位图对象的类

它需要访问位图资源,所以它需要访问我传递给它的构造函数的上下文,所以我像myClass(context)

理想情况下,我希望以某种方式注入依赖项。我想用一个工厂来实现这一点,这样我就可以向工厂请求对象,它会为我创建对象,但我只能从创建新窗口的活动类中获取上下文。因此,看来我必须在我主要活动的背景下继续传递

我最近问了一个类似的问题,他们告诉我根本不应该静态地调用上下文,应该始终传递上下文

这意味着如果我想测试这个类,我的测试类需要访问android框架(这意味着我不能在本地测试,必须使用AndroidTests)


这就是它的工作原理吗?我的类只需要一个位图,但由于上下文关系,我现在无法在本地进行测试。

如果你的类只需要一个位图,为什么你不能模拟一个位图?你应该在课堂上测试逻辑。您不需要测试
上下文
位图加载功能,因为我确信它已经在Android框架中测试过了。您已经对工厂模式有了很好的想法,因为您可以通过在应用程序中使用
myClass(bitmapFactory)
和在测试中使用
myClass(mockFactory)
注入模拟工厂进行测试


如果您完全确定需要编写一个测试来覆盖位图加载,那么请查看测试框架,因为它可以为类似这样的场景提供模拟上下文。

如果您需要
上下文的功能,那么您需要以某种方式将其传递到
MyClass
。但是,这并不意味着您需要静态存储
上下文。事实上,这将是一个非常糟糕的设计选择(看起来你已经知道了)

若您使用Dagger,那个么我有一个教程解释了如何构造依赖注入代码。这种结构的一部分实际上是处理
上下文
对象的复杂性

现在,即使
MyClass
需要访问
Context
才能获得位图,也不一定意味着您需要解析集成测试

首先,您可以使用Robolectric——这个库“模拟”Android框架,我认为它支持从资源中获取位图

然而,根据您的用例,还有一个更简单的选项。您真的需要一个特定的位图,还是只想确保
MyClass
在此位图上执行特定操作

如果您需要添加为资源的特定位图,请选择Robolectric,但如果不需要,则只需确保正确使用获得的位图,即可模拟位图

为了提高可读性、简化测试并防止违反Demeter定律,我建议您将
上下文
包装到此类中:

public class BitmapRetriever {
    private final Context mContext;

    public BitmapRetriever(Context context) { 
        mContext = context;
    }

    public Bitmap getBitmapById(int id) {
        // code that obtains bitmap
    }
}
然后使
MyClass
依赖
BitmapRetriever
而不是
Context


一旦您这样做,就可以直接通过测试中的mocked
BitmapRetriever
,当
MyClass
请求时,该测试将返回mocked
Bitmap
。然后,您可以在该模拟上断言各种条件。

这很有趣,但使用单独的位图检索器如何使测试更容易。@red888,它归结为单一责任原则。如果您的类接受上下文,那么理论上它可以访问其所有方法。你知道它只使用其中的一个,但是在你之后工作的人(或者六个月后的你自己)不会知道这一点。此外,将上下文传递到任何类都会破坏封装。通过将其包装到一个只负责一项任务的类中,您可以简化代码的读取和测试的编写,并消除将来有人也开始使用其他上下文方法的可能性。@red888,简而言之-如果您的类只需要位图,则没有理由让它访问整个上下文。这被称为违反德米特定律。@red888,更多关于单一责任原则和上下文:如果我用工厂构建myClass,我还会将上下文传递给创建对象的工厂吗?我想我必须这样做,因为我不能让工厂成为一个活动类,对吗?另外,为什么在上下文包装器中使用“final”?