2012-07-20 12 views
14

Estoy usando Java 6, JUnit 4.8.1 y escribiendo una aplicación de consola. Mi aplicación tiene un campo miembro que no esté expuesto ...Java: ¿Cómo me burlo de un método de un campo cuando ese campo no está expuesto?

public class MyApp { 
    ... 
    private OpportunitiesService m_oppsSvc; 

    private void initServices() { 
     … 
     m_oppsSvc = new OpportunitiesServiceImpl(…); 
    } 
    ... 
} 

Quiero burlarse de un comportamiento de tal manera que cada vez que uno de los métodos de mi servicio se llama, (por ejemplo m_oppsSvc.getResults()), siempre se devuelve el mismo resultado. ¿Cómo puedo hacer eso? No hay un método setter para el campo. Actualmente estoy trabajando con Mockito 1.8.4. ¿Es posible hacer esto con Mockito o algún otro framework simulado?

+0

¿Puede usted no crear un método de selección? – jrad

+0

Posible duplicado de [¿Cómo pruebas tu unidad de métodos privados] (http://programmers.stackexchange.com/questions/100959/how-do-you-unit-test-private-methods)? –

+0

¿Qué estás intentando probar? es 'getResults()' public? ¿Tiene un campo privado asociado o se calcula en tiempo de ejecución? Dependiendo de las respuestas, es posible que pueda usar un 'espía' o que la burla no tenga nada que ver con eso y podría usar algo como 'org.springframework.test.util.ReflectionTestUtils' para simplemente establecer el campo privado en el valor que desea – jhericks

Respuesta

12

Esto es lo que quiere:

@RunWith(MockitoJUnitRunner.class) 
public class MyAppTest { 

    @Mock private OpportunitiesService mocked_m_oppsSvc; 
    @InjectMocks MyApp myApp; 

    @Test public void when_MyApp_uses_OpportunititesService_then_verify_something() { 
     // given 
     given(mocked_m_oppsSvc.whatever()).willReturn(...); 

     // when 
     myApp.isUsingTheOpportunitiesService(...); 

     // then 
     verify... 
     assertThat... 
    } 
} 

Usando: Mockito 1.9.0, BDD style, FEST-Assert AssertJ.

Espero que ayude :)

+0

@InjectMocks solo admite la inyección de setter. – Matt

+3

@InjectMocks también puede inyectar campos privados. –

+1

Javadoc era incorrecto, siempre admitía la inyección de campos privados. Luego inyección setter, luego inyección de constructor en 1.9.0. Compruebe el Javadoc de InjectmMocks en la versión 1.9.0 http://docs.mockito.googlecode.com/hg/1.9.0/org/mockito/InjectMocks.html – Brice

-1

En general, debe usar la inyección de dependencia y pasar el objeto de simulación (de tipo OppportunitiesServiceImpl) en el constructor, un setter separado o directamente al método (getResults). Es posible que deba extraer una interfaz para OpportunitiesServiceImpl primero.

-1

Normalmente, esto se soluciona mediante el uso de inyección de dependencia. En el modo regular (producción), su contenedor de inyección de dependencia (por ejemplo, Spring o Guice) inyectará una instancia de OpportunitiesService en MyApp a través de su constructor o mediante un setter.

Luego, cuando está probando, puede "inyectar" una instancia simulada manualmente utilizando el mismo argumento setter o constructor.

En vez de hacer

m_oppsSvc = new OpportunitiesServiceImpl(…); 

Trate Pasando a través de OpportunitesService constructor MyApp 's

6

Debe tener en cuenta los intentos para burlarse de un campo privado de un smell. Es decir, una señal de que lo que estás tratando de hacer es incorrecto o que tu código está actualmente mal estructurado. Sólo es necesario para burlarse de los métodos públicos o dependencias inyectados

En el código que has dado debe tener en cuenta OpportunitiesService inyección de la siguiente manera:

public class MyApp { 
    ... 
    private OpportunitiesService m_oppsSvc; 

    public MyApp(OpportunitiesService oppsSvc) { 
     this.m_oppsSvc = oppsSvc; 
    } 
    ... 
} 

En la prueba A continuación, puede inyectarse una maqueta de la siguiente manera:

OpportunitiesService mockOpportunitiesService = 
    Mockito.mock(OpportunitiesService.class); 
Mockit.when(mockOpportunitiesService.someMethod()).thenReturn(someValue); 
MyApp app = new MyApp(mockOpportunitiesService); 
0

puede hacerlo fácilmente con JMockit:

public class MyAppTest 
{ 
    @Tested MyApp myApp; 

    @Test 
    public testSomething(final @Capturing OpportunitiesService mockService) 
    { 
     new NonStrictExpectations() {{ 
      mockService.getResults(); result = asList("a", "b", "C"); 
      // record other expectations, if needed 
     }}; 

     myApp.whateverMethodIWantToTest(); 

     new Verifications() {{ 
      mockService.doSomething(anyInt); 
      // verify other expectations, if needed 
     }}; 
    } 
} 

Aunque la clase de implementación OpportunitiesServiceImpl no se menciona en el código de prueba, sus instancias (cualquier número de ellas) seguirán burlándose.

6

Teniendo en cuenta que ya está usando Mockito, ¿por qué no utilizar la reflexión:

@RunWith(MockitoJUnitRunner.class) 
public class MyApp { 

    @Mock 
    private OpportunitiesService m_oppsSvc; 

    private MyApp myApp; 


    @Before 
    public void before() throws Exception { 
     myApp = new MyApp(); 
     Field f = MyApp.class.getDeclaredField("m_oppsSvc"); 
     f.setAccessible(true); 
     f.set(myApp, m_oppsSvc); 
    } 
} 

Es un poco feo, pero lo hará el truco.Tenga en cuenta que esta puede no ser la forma más eficiente de hacerlo con Mockito, pero funcionará.

También hay Powermock que debería permitirle hacer esto también con la clase Whitebox. No voy a entrar en detalles de todo el Powermock pero aquí está la llamada para inyectar el valor del campo privado, que debe ser un objeto de burla:

Whitebox.setInternalState(myApp, "m_oppsSvc", m_oppsSvc); 
Cuestiones relacionadas