tutorial programacion interfaz grafica español componentes codigo clase java unit-testing junit mocking jmockit

programacion - java swing tutorial pdf español



Simula un método privado en la misma clase que se está probando (5)

Tengo una clase de Java llamada, MyClass , que quiero probar con JUnit. El método público, methodA , que deseo probar llama a un método privado, methodB , en la misma clase para determinar qué ruta condicional seguir. Mi objetivo es escribir pruebas JUnit para las diferentes rutas en el methodA Además, methodB llama a un servicio, por lo que no quiero que se ejecute realmente cuando ejecuto las pruebas JUnit.

¿Cuál es la mejor manera de methodB método methodB y controlar su retorno para que pueda probar diferentes rutas para ''methodA''?

Prefiero usar JMockit cuando escribo simulacros, por lo que estoy específicamente interesado en cualquier respuesta que se aplique a JMockit.

Aquí está mi clase de ejemplo:

public class MyClass { public String methodA(CustomObject object1, CustomObject object2) { if(methodB(object1, object2)) { // Do something. return "Result"; } // Do something different. return "Different Result"; } private boolean methodB(CustomObject custObject1, CustomObject custObject2) { /* For the sake of this example, assume the CustomObject.getSomething() * method makes a service call and therefore is placed in this separate * method so that later an integration test can be written. */ Something thing1 = cobject1.getSomething(); Something thing2 = cobject2.getSomething(); if(thing1 == thing2) { return true; } return false; } }

Esto es lo que tengo hasta ahora:

public class MyClassTest { MyClass myClass = new MyClass(); @Test public void test_MyClass_methodA_enters_if_condition() { CustomObject object1 = new CustomObject("input1"); CustomObject object2 = new CustomObject("input2"); // How do I mock out methodB here to return true? assertEquals(myClass.methodA(object1, object2), "Result"); } @Test public void test_MyClass_methodA_skips_if_condition() { CustomObject object1 = new CustomObject("input1"); CustomObject object2 = new CustomObject("input2"); // How do I mock out methodB here to return false? assertEquals(myClass.methodA(object1, object2), "Different Result"); } }

¡Gracias!


Haga que methodB sea miembro de una clase separada y tenga una referencia privada a esa clase dentro de MyClass .

public class MyClass { private MyOtherClass otherObject = new MyOtherClass(); public String methodA(CustomObject object1, CustomObject object2) { if(otherObject.methodB(object1, object2)) { // Do something. return "Result"; } // Do something different. return "Different Result"; } } class MyOtherClass { public boolean methodB(CustomObject custObject1, CustomObject custObject2) { // Yada yada code } }

Personalmente, generalmente solo pruebo los métodos públicos y miro los informes de cobertura para asegurarme de que se hayan visitado todas las rutas en mis métodos privados. Si realmente necesito probar un método privado, es un olor que requiere una refactorización como el anterior.

También podrías usar el reflejo, pero me sentiría sucio haciendo eso. Si REALMENTE quiere la solución, déjeme saber y la agregaré a esta respuesta.


No tengas la tentación de burlarte de los métodos privados, incluso si puedes hacer trucos para hacerlo con una herramienta de burla. Los miembros privados son detalles de implementación, que usted debe poder cambiar libremente. En su lugar, use la API no privada para ejercitar la clase. Si esto es problemático, considere mover el código problemático a una clase diferente, si aún no está allí, y use la inyección de dependencia para inyectar una implementación simulada del código problemático.


Para burlarse del método privado, necesitas powermock
El código de ejemplo será así, pero no lo he ejecutado.

import org.mockito.Mockito; import org.powermock.api.mockito.PowerMockito; import org.powermock.modules.junit4.PowerMockRunner; @RunWith (PowerMockRunner.class) public class MyClassTest { @Test public void test_MyClass_methodA_enters_if_condition() { final MyClass myClass = Mockito.mock (MyClass.class); CustomObject object1 = new CustomObject("input1"); CustomObject object2 = new CustomObject("input2"); Mockito.when (myClass.methodB(object1, object2)).thenReturn (true); Mockito.when (myClass.methodA(object1, object2)).thenCallRealMethod (); assertEquals(myClass.methodA(object1, object2), "Result"); } }


Para dar la respuesta que pediste (usando la burla parcial de JMockit):

public class MyClassTest { @Tested MyClass myClass; @Test public void test_MyClass_methodA_enters_if_condition() { final CustomObject object1 = new CustomObject("input1"); final CustomObject object2 = new CustomObject("input2"); new NonStrictExpectations(myClass) {{ invoke(myClass, "methodB", object1, object2); result = true; }}; assertEquals("Result", myClass.methodA(object1, object2)); } @Test public void test_MyClass_methodA_skips_if_condition() { final CustomObject object1 = new CustomObject("input1"); final CustomObject object2 = new CustomObject("input2"); new NonStrictExpectations(myClass) {{ invoke(myClass, "methodB", object1, object2); result = false; }}; assertEquals("Different Result", myClass.methodA(object1, object2)); } }

Sin embargo, no recomendaría hacerlo así. En general, private métodos private no deberían ser burlados. En cambio, simule la dependencia externa real de su unidad bajo prueba (el CustomObject en este caso):

public class MyTestClass { @Tested MyClass myClass; @Mocked CustomObject object1; @Mocked CustomObject object2; @Test public void test_MyClass_methodA_enters_if_condition() { new NonStrictExpectations() {{ Something thing = new Something(); object1.getSomething(); result = thing; object2.getSomething(); result = thing; }}; assertEquals("Result", myClass.methodA(object1, object2)); } @Test public void test_MyClass_methodA_skips_if_condition() { new NonStrictExpectations() {{ object1.getSomething(); result = new Something(); object2.getSomething(); result = new Something(); }}; assertEquals("Different Result", myClass.methodA(object1, object2)); } }


import org.easymock.EasyMock; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.powermock.api.easymock.PowerMock; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; @RunWith(PowerMockRunner.class) @PrepareForTest({ MyClass.class }) public class MyClassTest { // Class Under Test MyClass cut; @Before public void setUp() { // Create a new instance of the service under test (SUT). cut = new MyClass(); // Common Setup // TODO } @Test public void testMethodA() throws Exception { /* Initialization */ CustomObject object2 = PowerMock.createNiceMock(CustomObject.class); CustomObject object1 = PowerMock.createNiceMock(CustomObject.class); MyClass partialMockCUT = PowerMock.createPartialMock(MyClass.class, "methodB"); long response = 1; /* Mock Setup */ PowerMock .expectPrivate(partialMockCUT, "methodB", EasyMock.isA(CustomObject.class), EasyMock.isA(CustomObject.class)).andReturn(true) .anyTimes(); /* Mock Setup */ /* Activate the Mocks */ PowerMock.replayAll(); /* Test Method */ String result = partialMockCUT.methodA(object1, object2); /* Asserts */ Assert.assertNotNull(result); PowerMock.verifyAll(); } }