Creación falsa de objeto dentro del método

Descripción del problema

Estoy tratando de simular la creación de objetos dentro del método. Tengo LoginFragment que está creando LoginPresenterImpl dentro del método onCreate , como se muestra a continuación:

 public class LoginFragment extends BaseFragment { private LoginPresenter mPresenter; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mPresenter = new LoginPresenterImpl(this); <<-- Should be mocked } } 

Tengo algunos problemas con la combinación de RobolectricGradleTestRunner y PowerMockRunner en una prueba, pero después de leer este post, he encontrado la manera de hacerlo, por lo que mi mirada de prueba como este:

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

    Esto es sólo un código malo y lo contrario de lo que es "Dependency Injection" patrón.

    Si utilizó el marco de la inyección de la dependencia como Dagger , tal problema no sucedería mientras que todo utilizado clasificado sería inyectado.

    En los casos de prueba, anularía los módulos para proporcionar burlas en lugar de clases reales:

     @Module public class TestDataModule extends DataModule { public TestDataModule(Application application) { super(application); } @Override public DatabaseManager provideDatabaseManager(DatabaseUtil databaseUtil) { return mock(DatabaseManager.class); } } 

    Y sólo se burlan de su comportamiento.

    Las reglas SÓLIDAS son realmente importantes para mantener el código comprobable y reutilizable.

    Esto podría funcionar, no tengo manera de probarlo aunque …

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

    Entonces en su Test.java

     private LoginFragment createLoginFragment() { LoginFragment loginFragment = LoginFragmentTest.newInstance(); return loginFragment; } private static class LoginFragmentTest extends LoginFragment { @Override protected LoginPresenter getLoginPresenter() { return mPresenterMock; } } 

    En primer lugar, si no puedes cambiar este código fuente, mi respuesta no me ayudará y tendrás que volver a las pesadas herramientas de burla.

    Desde el estilo de codificación y la perspectiva de diseño, recomendaría definir una fábrica que crea una instancia de LoginPresenter. Puede solicitar esta fábrica en el constructor de LoginFragment. Y luego usar esta fábrica en el método onCreate. A continuación, puede utilizar su propia implementación de esta fábrica en las pruebas de unidad que creará la implementación de prueba de LoginPresenter. Es un enfoque POJO que hace que su código sea comprobable.

    Por ejemplo

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

    Supongo que no puede cambiar el código de producción. Por lo que debido a este mal diseño es difícil lograr su requisito de una manera adecuada.

    Pero hay una manera sucia de hacer esto, usar la reflexión para asignar valor al campo privado.

     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; } 
    FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.