我正在尝试在方法内部模拟对象创建。
我LoginFragment
正在LoginPresenterImpl
方法中创建onCreate
,如下所示:
public class LoginFragment extends BaseFragment {
private LoginPresenter mPresenter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPresenter = new LoginPresenterImpl(this); <<-- Should be mocked
}
}
我在一次测试中将RobolectricGradleTestRunner
和PowerMockRunner
结合起来时遇到了一些问题,但在阅读this帖后,我找到了如何做到的方法,所以我的测试看起来像这样:
@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 21)
@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*"})
public abstract class BaseRobolectricTest {
}
@PrepareForTest({LoginPresenterImpl.class})
public class Test extends BaseRobolectricTest {
private LoginPresenterImpl mPresenterMock;
@Rule
public PowerMockRule rule = new PowerMockRule();
@Before
public void setup() {
mockStatic(LoginPresenterImpl.class);
mPresenterMock = PowerMockito.mock(LoginPresenterImpl.class);
}
@Test
public void testing() throws Exception {
when(mPresenterMock.loadUsername(any(Context.class))).thenReturn(VALID_USERNAME);
when(mPresenterMock.loadPassword(any(Context.class))).thenReturn(VALID_PASSWORD);
when(mPresenterMock.canAutologin(VALID_USERNAME, VALID_PASSWORD)).thenReturn(true);
whenNew(LoginPresenterImpl.class).withAnyArguments().thenReturn(mPresenterMock);
FragmentTestUtil.startFragment(createLoginFragment());
}
private LoginFragment createLoginFragment() {
LoginFragment loginFragment = LoginFragment.newInstance();
return loginFragment;
}
}
答案 0 :(得分:4)
这只是一个错误的代码,与“依赖注入”模式相反。
如果您使用了像Dagger
这样的依赖注入框架,那么这样的问题就不会发生,因为所有使用过的类都会被注入。
在测试用例中,您将覆盖模块以提供模拟而不是真正的类:
@Module
public class TestDataModule extends DataModule {
public TestDataModule(Application application) {
super(application);
}
@Override
public DatabaseManager provideDatabaseManager(DatabaseUtil databaseUtil) {
return mock(DatabaseManager.class);
}
}
只是模仿他们的行为。
SOLID rules对于维护可测试和可重用的代码非常重要。
答案 1 :(得分:1)
这可能有效,但我无法测试它......
public class LoginFragment extends BaseFragment {
private LoginPresenter mPresenter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPresenter = getLoginPresenter();
}
protected LoginPresenter getLoginPresenter() {
return new LoginPresenterImpl(this);
}
}
然后在Test.java
private LoginFragment createLoginFragment() {
LoginFragment loginFragment = LoginFragmentTest.newInstance();
return loginFragment;
}
private static class LoginFragmentTest extends LoginFragment {
@Override
protected LoginPresenter getLoginPresenter() {
return mPresenterMock;
}
}
答案 2 :(得分:1)
首先,如果您无法更改此源代码,我的答案将无济于事,您必须返回沉重的模拟工具。
从编码风格和设计角度来看,我建议定义一个创建LoginPresenter实例的工厂。您可以在LoginFragment构造函数中请求此工厂。然后在onCreate方法中使用此工厂。 然后,您可以在单元测试中使用您自己的工厂实现,这将创建LoginPresenter的测试实现。 这是一种使您的代码可以测试的POJO方法。
例如
public class LoginFragment extends BaseFragment {
private LoginPresenter mPresenter;
private final LoginPresenterFactory presenterFactory;
public LoginFragment(LoginPresenterFactory presenterFactory) {
this.presenterFactory = presenterFactory;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPresenter = presenterFactory.create();
}
}
答案 3 :(得分:1)
我假设您无法更改生产代码。 因此,由于这种糟糕的设计,很难以正确的方式达到您的要求。
但是有一种肮脏的方式来做到这一点, 使用反射为私有字段赋值。
public class ReflectionUtility
{
public static void setValue(Object obj, String fieldName, Object value)
{
try
{
final Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
}
catch (IllegalAccessException e)
{
throw new RuntimeException(e);
}
catch (NoSuchFieldException e)
{
throw new RuntimeException(e);
}
}
}
then in your test,
private LoginFragment createLoginFragment()
{
LoginFragment loginFragment = LoginFragment.newInstance();
ReflectionUtility.setValue(loginFragment, "mPresenter", mPresenterMock);
return loginFragment;
}