java 如何模拟 getApplicationContext
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5686194/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to mock getApplicationContext
提问by lstipakov
I have an application that stores app context information. The app context information is shared between activities in MyApp class which extends Application class.
我有一个存储应用程序上下文信息的应用程序。应用上下文信息在扩展 Application 类的 MyApp 类中的活动之间共享。
I am writing a unit test for my activity, and I want to check that when user clicks a button in the activity, an application state will change. Something like this:
我正在为我的活动编写单元测试,我想检查当用户单击活动中的按钮时,应用程序状态是否会发生变化。像这样的东西:
@Override
public void onClick(View pView) {
((MyApp)getApplicationContext()).setNewState();
}
The problem is that I don't know how to mock that application context. I am using ActivityUnitTestCaseas a test case base. When I call setApplication, it changes the value of mApplicationmember of Activityclass, but not application context. I've tried setActivityContextalso, but it seems wrong (it is not app context but activity context) and it fires assert inside startActivity).
问题是我不知道如何模拟该应用程序上下文。我使用ActivityUnitTestCase作为测试用例库。当我调用setApplication 时,它会更改Activity类的mApplication成员的值,但不会更改应用程序上下文。我也尝试过setActivityContext,但它似乎是错误的(它不是应用程序上下文,而是活动上下文)并且它在startActivity 中触发 assert )。
So the question is - how to mock getApplicationContext()?
所以问题是 - 如何模拟getApplicationContext()?
回答by Spoike
Since the method getApplicationContext
is inside the class that you're extending it becomes somewhat problematic. There are a couple of problems to consider:
由于该方法getApplicationContext
位于您要扩展的类中,因此它变得有些问题。有几个问题需要考虑:
- You really can't mock a class that is under test, which is one of the many drawbacks with object inheritance (i.e. subclassing).
- The other problem is that
ApplicationContext
is a singleton, which makes it all more evil to test since you can't easily mock out a global state that is programmed to be irreplaceable.
- 您真的无法模拟正在测试的类,这是对象继承(即子类化)的众多缺点之一。
- 另一个问题是这
ApplicationContext
是一个单例,这使得测试变得更加邪恶,因为你不能轻易地模拟一个被编程为不可替代的全局状态。
What you can do in this situation is to prefer object composition over inheritance. So in order to make your Activity
testable you need to split up the logic a little. Lets say that your Activity
is called MyActivity
. It needs to be composedof a logic component (or class), lets name it MyActivityLogic
. Here is a simple class-diagram figure:
在这种情况下,您可以做的是更喜欢对象组合而不是继承。因此,为了使您的Activity
可测试,您需要稍微拆分逻辑。让我们说你Activity
被称为MyActivity
. 它需要由一个逻辑组件(或类)组成,让我们将其命名为MyActivityLogic
. 这是一个简单的类图:
To solve the singleton problem, we let the logic be "injected" with an application context, so it can be tested with a mock. We then only need to test that the MyActivity
object has put the correct application context into MyActivityLogic
. How we basically solve both problems is through another layer of abstraction(paraphrased from Butler Lampson). The new layer we add in this case is the activity logic moved outside of the activity object.
为了解决单例问题,我们让逻辑“注入”了一个应用程序上下文,这样它就可以用模拟来测试。然后我们只需要测试该MyActivity
对象是否已将正确的应用程序上下文放入MyActivityLogic
. 我们如何基本上解决这两个问题是通过另一个抽象层(从巴特勒兰普森转述)。我们在这种情况下添加的新层是移动到活动对象之外的活动逻辑。
For the sake of your example the classes need to look sort-of like this:
为了您的示例,类需要看起来像这样:
public final class MyActivityLogic {
private MyApp mMyApp;
public MyActivityLogic(MyApp pMyApp) {
mMyApp = pMyApp;
}
public MyApp getMyApp() {
return mMyApp;
}
public void onClick(View pView) {
getMyApp().setNewState();
}
}
public final class MyActivity extends Activity {
// The activity logic is in mLogic
private final MyActivityLogic mLogic;
// Logic is created in constructor
public MyActivity() {
super();
mLogic = new MyActivityLogic(
(MyApp) getApplicationContext());
}
// Getter, you could make a setter as well, but I leave
// that as an exercise for you
public MyActivityLogic getMyActivityLogic() {
return mLogic;
}
// The method to be tested
public void onClick(View pView) {
mLogic.onClick(pView);
}
// Surely you have other code here...
}
It should all look something like this:
它应该看起来像这样:
To test MyActivityLogic
you will only need a simple jUnit TestCase
instead of the ActivityUnitTestCase
(since it isn't an Activity), and you can mock your application context using your mocking framework of choice (since handrollingyour own mocks is a bit of a drag). Example uses Mockito:
要进行测试,MyActivityLogic
您只需要一个简单的 jUnitTestCase
而不是ActivityUnitTestCase
(因为它不是活动),并且您可以使用您选择的模拟框架来模拟您的应用程序上下文(因为手动滚动您自己的模拟有点麻烦)。示例使用Mockito:
MyActivityLogic mLogic; // The CUT, Component Under Test
MyApplication mMyApplication; // Will be mocked
protected void setUp() {
// Create the mock using mockito.
mMyApplication = mock(MyApplication.class);
// "Inject" the mock into the CUT
mLogic = new MyActivityLogic(mMyApplication);
}
public void testOnClickShouldSetNewStateOnAppContext() {
// Test composed of the three A's
// ARRANGE: Most stuff is already done in setUp
// ACT: Do the test by calling the logic
mLogic.onClick(null);
// ASSERT: Make sure the application.setNewState is called
verify(mMyApplication).setNewState();
}
To test the MyActivity
you use ActivityUnitTestCase
as usual, we only need to make sure that it creates a MyActivityLogic
with the correct ApplicationContext
. Sketchy test code example that does all this:
为了像往常一样测试MyActivity
你使用的ActivityUnitTestCase
,我们只需要确保它创建了一个MyActivityLogic
正确的ApplicationContext
. 完成所有这些的粗略测试代码示例:
// ARRANGE:
MyActivity vMyActivity = getActivity();
MyApp expectedAppContext = vMyActivity.getApplicationContext();
// ACT:
// No need to "act" much since MyActivityLogic object is created in the
// constructor of the activity
MyActivityLogic vLogic = vMyActivity.getMyActivityLogic();
// ASSERT: Make sure the same ApplicationContext singleton is inside
// the MyActivityLogic object
MyApp actualAppContext = vLogic.getMyApp();
assertSame(expectedAppContext, actualAppContext);
Hope it all makes sense to you and helps you out.
希望这一切对你有意义,并帮助你。