Reputation:
I'm trying to mock object creation inside of method.
I have LoginFragment
which is creating LoginPresenterImpl
inside of onCreate
method, like shown below:
public class LoginFragment extends BaseFragment {
private LoginPresenter mPresenter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPresenter = new LoginPresenterImpl(this); <<-- Should be mocked
}
}
I have some problems with combining RobolectricGradleTestRunner
and PowerMockRunner
in one test but after reading this post, I found way how to do that, so my test look like 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;
}
}
Upvotes: 5
Views: 13037
Reputation: 4173
I assume that you can not change the production code. so due to this bad design it is difficult achieve your requirement using a proper way.
But there is a dirty way to do this, use reflection to assign value to the private field.
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;
}
Upvotes: 1
Reputation: 2565
Firstly if you cannot change this source code my answer will not help and you have to return to heavy mocking tools.
From coding style and design perspective I would recommend to define a factory that creates an instance of LoginPresenter. You can ask for this factory in LoginFragment constructor. And then use this factory in onCreate method. Then you can use your own implementation of this factory in unit tests that will create test implementation of LoginPresenter. That is a POJO approach that makes your code testable.
For example
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();
}
}
Upvotes: 1
Reputation: 549
This might work, I have no way to test it though...
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);
}
}
Then in your Test.java
private LoginFragment createLoginFragment() {
LoginFragment loginFragment = LoginFragmentTest.newInstance();
return loginFragment;
}
private static class LoginFragmentTest extends LoginFragment {
@Override
protected LoginPresenter getLoginPresenter() {
return mPresenterMock;
}
}
Upvotes: 1
Reputation: 20258
This is just a bad code and opposite of what is "Dependency Injection" pattern.
If you used dependency injection framework like Dagger
, such problem wouldn't happen as all used classed would be injected.
In test cases you would override your modules to provide mocks instead of real classes:
@Module
public class TestDataModule extends DataModule {
public TestDataModule(Application application) {
super(application);
}
@Override
public DatabaseManager provideDatabaseManager(DatabaseUtil databaseUtil) {
return mock(DatabaseManager.class);
}
}
And just mock their behavior.
SOLID rules are really important in mainaining testable and reusable code.
Upvotes: 5