Mock creation of Object inside method

11,945

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;
}
Share:
11,945
Admin
Author by

Admin

Updated on June 13, 2022

Comments

  • Admin
    Admin about 2 years

    Problem Description

    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:

    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;
        }
    }