Mock creation of Object inside method
Solution 1
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.
Solution 2
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();
}
}
Solution 3
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;
}
}
Solution 4
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;
}
![Admin](/assets/logo_square_200-5d0d61d6853298bd2a4fe063103715b4daf2819fc21225efa21dfb93e61952ea.png)
Admin
Updated on June 13, 2022Comments
-
Admin about 2 years
Problem Description
I'm trying to mock object creation inside of method. I have
LoginFragment
which is creatingLoginPresenterImpl
inside ofonCreate
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
andPowerMockRunner
in one test but after reading this post, I found way how to do that, so my test look like this:BaseRobolectricTest.java
@RunWith(PowerMockRunner.class) @PowerMockRunnerDelegate(RobolectricGradleTestRunner.class) @Config(constants = BuildConfig.class, sdk = 21) @PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*"}) public abstract class BaseRobolectricTest { }
Test.java
@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; } }