DP_*_*DP_ 2 java android unit-testing mocking mockito
我有一个Android活动,其中有一些调用
final ConnectToServerAsyncTask task = new ConnectToServerAsyncTask(...);
Run Code Online (Sandbox Code Playgroud)
和
final Intent intent = new Intent(this, SomeActivity.class);
Run Code Online (Sandbox Code Playgroud)
为了单元测试这个类,我需要能够嘲笑的创建ConnectToServerAsyncTask
和Intent
(例如使用的Mockito).
有比下面描述的方法更优雅的方法吗?
public class MainActivityOfTheApp extends Activity {
private IAsyncTaskFactory asyncTaskFactory = new AsyncTaskFactory();
private IIntentFactory intentFactory = new IntentFactory();
public void setAsyncTaskFactory(final IAsyncTaskFactory aFactory)
{
asyncTaskFactory = aFactory;
}
public void setIntentFactory(final IIntentFactory aFactory)
{
intentFactory = aFactory;
}
@Override
protected void onResume() {
...
final ConnectToServerAsyncTask task = asyncTaskFactory.create(...);
...
final Intent intent = intentFactory.create(this, OtherActivity.class);
...
}
}
Run Code Online (Sandbox Code Playgroud)
在单元测试中,我将创建一个MainActivityOfTheApp实例,然后使用setAsyncTaskFactory
和注入模拟setIntentFactory
.
您可以使用PowerMock模拟构造函数.
所以在你的情况下,测试看起来像这样:
@RunWith(PowerMockRunner.class)
@PrepareForTest(MainActivityOfTheApp.class)
public class MainActivityOfTheAppTest{
private AsyncTaskFactory asyncTaskFactory;
private IntentFactory intentFactory;
private MainActivityOfTheApp mainActivityOfTheApp;
@Before
public void prepare() {
asyncTaskFactory = PowerMockito.mock(AsyncTaskFactory.class);
intentFactory = PowerMockito.mock(IntentFactory.class);
PowerMockito.whenNew(AsyncTaskFactory.class).withNoArguments().thenReturn(asyncTaskFactory);
PowerMockito.whenNew(IntentFactory.class).withNoArguments().thenReturn(intentFactory);
mainActivityOfTheApp = new MainActivityOfTheApp();
}
@Test
public void doTest() {
//mainActivityOfTheApp has the mocks in final field inside, no need for the setters.
}
}
Run Code Online (Sandbox Code Playgroud)
我必须指出,PowerMock功能强大,但经常遇到复杂的类加载(例如OSGI环境)或代码覆盖工具(例如jacoco)的问题.它通常有效,但我浪费了一些时间.
第二种可能性是创建包私有(无修饰符.为什么不保护或公共?看看构造函数中的可覆盖方法调用有什么问题?)方法,它们调用构造函数如下:
public class MainActivityOfTheApp extends Activity {
private final IAsyncTaskFactory asyncTaskFactory = constructAsyncTaskFactory();
private final IIntentFactory intentFactory = constructIntentFactory();
IAsyncTaskFactory constructAsyncTaskFactory()
{
return new AsyncTaskFactory();
}
IIntentFactory constructIntentFactory()
{
return new IntentFactory();
}
...
}
Run Code Online (Sandbox Code Playgroud)
然后在你的单元测试中(测试必须与测试类在同一个包中!)你重写构造*()方法:
public class MainActivityOfTheAppTest{
private AsyncTaskFactory asyncTaskFactory;
private IntentFactory intentFactory;
private MainActivityOfTheApp mainActivityOfTheApp;
@Before
public void prepare() {
asyncTaskFactory = mock(AsyncTaskFactory.class);
intentFactory = mock(IntentFactory.class);
mainActivityOfTheApp = new HackedMainActivityOfTheApp();
}
@Test
public void doTest() {
//mainActivityOfTheApp has the mocks in final field inside, no need for the setters.
}
private class HackedMainActivityOfTheApp extends MainActivityOfTheApp {
IAsyncTaskFactory constructAsyncTaskFactory()
{
return asyncTaskFactory;
}
IIntentFactory constructIntentFactory()
{
return intentFactory;
}
}
}
Run Code Online (Sandbox Code Playgroud)
如果您对包含MainActivityOfTheApp的jar进行签名,则可以保护construct*方法.请参阅/sf/answers/67802871/:
此外,补丁更难(你必须重新签署jar),类补丁是不可能的(单个包中的所有类必须具有相同的签名源)并且拆分罐变成一件苦差事.