Android
Pruebas unitarias en Android con JUnit.
Buscar..
Observaciones
- Vogella: Pruebas unitarias con JUnit
- Anotaciones de Junit: java2novice.com
- Clase de afirmación : junit.org
- JUnit Api: tutorialspoint.com
- Anroid probando posts de Medium.com
Creando pruebas unitarias locales
Coloque sus clases de prueba aquí: /src/test/<pkg_name>/
Ejemplo de clase de prueba
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
int a=4, b=5, c;
c = a + b;
assertEquals(9, c); // This test passes
assertEquals(10, c); //Test fails
}
}
Descompostura
public class ExampleUnitTest {
...
}
La clase de prueba, puede crear varias clases de prueba y colocarlas dentro del paquete de prueba.
@Test
public void addition_isCorrect() {
...
}
El método de prueba, varios métodos de prueba se pueden crear dentro de una clase de prueba.
Observe la anotación @Test
.
La anotación de prueba le dice a JUnit que el método de anulación público al que se adjunta se puede ejecutar como un caso de prueba.
Hay varias otras anotaciones útiles como @Before
, @After
etc. Esta página sería un buen lugar para comenzar.
assertEquals(9, c); // This test passes
assertEquals(10, c); //Test fails
Estos métodos son miembros de la clase Assert
. Algunos otros métodos útiles son assertFalse()
, assertNotNull()
, assertTrue
etc. Aquí hay una explicación detallada.
Información de anotación para JUnit Test:
Prueba @: La anotación de prueba le dice a JUnit que el método de anulación público al que está adjunto se puede ejecutar como un caso de prueba. Para ejecutar el método, JUnit primero construye una nueva instancia de la clase y luego invoca el método anotado.
@Antes de: Al escribir pruebas, es común encontrar que varias pruebas necesitan que se creen objetos similares antes de que puedan ejecutarse. La anotación de un método void público con @Before
hace que ese método se ejecute antes que el método Test.
@ Después : si asigna recursos externos en un método Antes, debe liberarlos después de que se ejecute la prueba. La anotación de un método de vacío público con @After
hace que ese método se ejecute después del método de prueba. Se garantiza que todos los métodos @After
se ejecutarán incluso si un método Antes o Prueba arroja una excepción
Consejo: crea rápidamente clases de prueba en Android Studio
- Coloque el cursor en el nombre de la clase para la que desea crear una clase de prueba.
- Presione Alt + Enter (Windows).
- Seleccione Crear prueba, pulse Retorno.
- Seleccione los métodos para los que desea crear métodos de prueba, haga clic en Aceptar.
- Seleccione el directorio donde desea crear la clase de prueba.
- Ya terminaste, esto lo que obtienes es tu primera prueba.
Sugerencia: Ejecutar pruebas fácilmente en Android Studio.
- Haga clic derecho para probar el paquete.
- Seleccione Run 'Tests en ...
- Todas las pruebas en el paquete se ejecutarán a la vez.
Moviendo la lógica de negocios fuera de los componentes de Android
Gran parte del valor de las pruebas unitarias de JVM locales proviene de la forma en que diseña su aplicación. Debe diseñarlo de tal manera que pueda desacoplar la lógica de su negocio de sus componentes de Android. Este es un ejemplo de tal manera de usar el patrón Model-View-Presenter . Practiquemos esto implementando una pantalla de registro básica que solo requiere un nombre de usuario y contraseña. Nuestra aplicación de Android es responsable de validar que el nombre de usuario que el usuario proporciona no está en blanco y que la contraseña tiene al menos ocho caracteres y contiene al menos un dígito. Si el nombre de usuario / contraseña es válido, realizamos nuestra llamada a la API de registro, de lo contrario, mostramos un mensaje de error.
Ejemplo donde la lógica de negocios está altamente acoplada con el componente Android.
public class LoginActivity extends Activity{
...
private void onSubmitButtonClicked(){
String username = findViewById(R.id.username).getText().toString();
String password = findViewById(R.id.password).getText().toString();
boolean isUsernameValid = username != null && username.trim().length() != 0;
boolean isPasswordValid = password != null && password.trim().length() >= 8 && password.matches(".*\\d+.*");
if(isUsernameValid && isPasswordValid){
performSignUpApiCall(username, password);
} else {
displayInvalidCredentialsErrorMessage();
}
}
}
Ejemplo donde se desacopla la lógica de negocios del componente Android.
Aquí definimos en una sola clase, LoginContract, que albergará las diversas interacciones entre nuestras diversas clases.
public interface LoginContract {
public interface View {
performSignUpApiCall(String username, String password);
displayInvalidCredentialsErrorMessage();
}
public interface Presenter {
void validateUserCredentials(String username, String password);
}
}
Nuestra actividad de inicio de sesión es, en su mayor parte, la misma, excepto que hemos eliminado la responsabilidad de tener que saber cómo validar el formulario de registro de un usuario (nuestra lógica empresarial). LoginActivity ahora se basará en nuestro nuevo LoginPresenter para realizar la validación.
public class LoginActivity extends Activity implements LoginContract.View{
private LoginContract.Presenter presenter;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
presenter = new LoginPresenter(this);
....
}
...
private void onSubmitButtonClicked(){
String username = findViewById(R.id.username).getText().toString();
String password = findViewById(R.id.password).getText().toString();
presenter.validateUserCredentials(username, password);
}
...
}
Ahora su lógica de negocios residirá en su nueva clase LoginPresenter.
public class LoginPresenter implements LoginContract.Presenter{
private LoginContract.View view;
public LoginPresenter(LoginContract.View view){
this.view = view;
}
public void validateUserCredentials(String username, String password){
boolean isUsernameValid = username != null && username.trim().length() != 0;
boolean isPasswordValid = password != null && password.trim().length() >= 8 && password.matches(".*\\d+.*");
if(isUsernameValid && isPasswordValid){
view.performSignUpApiCall(username, password);
} else {
view.displayInvalidCredentialsErrorMessage();
}
}
}
Y ahora podemos crear pruebas locales de unidades JVM contra su nueva clase LoginPresenter.
public class LoginPresenterTest {
@Mock
LoginContract.View view;
private LoginPresenter presenter;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
presenter = new LoginPresenter(view);
}
@Test
public void test_validateUserCredentials_userDidNotEnterUsername_displayErrorMessage() throws Exception {
String username = "";
String password = "kingslayer1";
presenter.validateUserCredentials(username, password);
Mockito.verify(view). displayInvalidCredentialsErrorMessage();
}
@Test
public void test_validateUserCredentials_userEnteredFourLettersAndOneDigitPassword_displayErrorMessage() throws Exception {
String username = "Jaime Lanninster";
String password = "king1";
presenter.validateUserCredentials(username, password);
Mockito.verify(view). displayInvalidCredentialsErrorMessage();
}
@Test
public void test_validateUserCredentials_userEnteredNineLettersButNoDigitsPassword_displayErrorMessage() throws Exception {
String username = "Jaime Lanninster";
String password = "kingslayer";
presenter.validateUserCredentials(username, password);
Mockito.verify(view). displayInvalidCredentialsErrorMessage();
}
@Test
public void test_validateUserCredentials_userEnteredNineLettersButOneDigitPassword_performApiCallToSignUpUser() throws Exception {
String username = "Jaime Lanninster";
String password = "kingslayer1";
presenter.validateUserCredentials(username, password);
Mockito.verify(view).performSignUpApiCall(username, password);
}
}
Como se puede ver, cuando se extrajeron nuestra lógica de negocio fuera de la LoginActivity y lo colocó en el LoginPresenter POJO . Ahora podemos crear pruebas locales de unidades JVM contra nuestra lógica empresarial.
Se debe tener en cuenta que nuestro cambio en la arquitectura tiene varias otras implicaciones, ya que estamos cerca de adherirnos a cada clase que tiene una responsabilidad única, clases adicionales, etc. Estos son solo efectos secundarios de la forma en que elijo hacerlo. desacoplamiento a través del estilo MVP. MVP es solo una forma de hacer esto, pero hay otras alternativas que tal vez quiera ver, como MVVM . Solo tienes que elegir el mejor sistema que funcione para ti.
Empezando con JUnit
Preparar
Para iniciar la prueba de unidad de su proyecto de Android usando JUnit, debe agregar la dependencia de JUnit a su proyecto y debe crear un conjunto de fuente de prueba que contendrá el código fuente de las pruebas de unidad. Los proyectos creados con Android Studio a menudo ya incluyen la dependencia de JUnit y el conjunto de fuentes de prueba
Agregue la siguiente línea a su módulo build.gradle
en el Closure
dependencias:
testCompile 'junit:junit:4.12'
Las clases de prueba de JUnit están ubicadas en un conjunto de fuente especial denominado test
. Si este conjunto de fuentes no existe, debe crear una nueva carpeta usted mismo. La estructura de carpetas de un proyecto predeterminado de Android Studio (basado en Gradle) se ve así:
<project-root-folder>
/app (module root folder)
/build
/libs
/src
/main (source code)
/test (unit test source code)
/androidTest (instrumentation test source code)
build.gradle (module gradle file)
/build
/gradle
build.gradle (project gradle file)
gradle.properties
gradlew
gradlew.bat
local.properties
settings.gradle (gradle settings)
Si su proyecto no tiene la carpeta /app/src/test
, debe crearlo usted mismo. Dentro de la carpeta de test
también necesita una carpeta java
(créela si no existe). La carpeta java en el conjunto de fuentes de test
debe contener la misma estructura de paquetes que su conjunto de fuentes main
.
Si configura correctamente la estructura de su proyecto (en la vista de Android en Android Studio) debería tener este aspecto:
Nota: No es necesario que tenga el androidTest
fuentes de androidTest
, este conjunto de fuentes se encuentra a menudo en proyectos creados por Android Studio y se incluye aquí como referencia.
Escribiendo una prueba
Crear una nueva clase dentro del conjunto de fuentes de
test
.
Haga clic con el botón derecho en el conjunto de fuentes de prueba en la vista del proyecto, elijaNew
>Java class
.
El patrón de nomenclatura más utilizado es usar el nombre de la clase que se va a probar con laTest
agregada. Así queStringUtilities
convierte enStringUtilitiesTest
.Añadir la anotación
@RunWith
La anotación@RunWith
es necesaria para que JUnit ejecute las pruebas que vamos a definir en nuestra clase de prueba. El corredor de JUnit predeterminado (para JUnit 4) es elBlockJUnit4ClassRunner
pero en lugar de usar esta ejecución directamente, es más conveniente usar el aliasJUnit4
que es una abreviatura para el corredor de JUnit predeterminado.@RunWith(JUnit4.class) public class StringUtilitiesTest { }
Crear una prueba
Una prueba de unidad es esencialmente un método que, en la mayoría de los casos, no debería fallar si se ejecuta. En otras palabras, no debe lanzar una excepción. Dentro de un método de prueba, casi siempre encontrará afirmaciones que verifican si se cumplen las condiciones específicas. Si una aserción falla, lanza una excepción que hace que el método / prueba falle. Un método de prueba siempre se anota con la anotación@Test
. Sin esta anotación, JUnit no ejecutará automáticamente la prueba.@RunWith(JUnit4.class) public class StringUtilitiesTest { @Test public void addition_isCorrect() throws Exception { assertEquals("Hello JUnit", "Hello" + " " + "JUnit"); } }
Nota: a diferencia del método estándar de Java, los nombres de los métodos de prueba de la unidad de convención de nomenclatura suelen contener guiones bajos.
Haciendo una prueba
Método
Para ejecutar un solo método de prueba, puede hacer clic con el botón derecho en el método y hacer clic enRun 'addition_isCorrect()'
o usar el método abreviado de tecladoctrl+shift+f10
.Si todo está configurado correctamente, JUnit comienza a ejecutar el método y debería ver la siguiente interfaz dentro de Android Studio:
Clase
También puede ejecutar todas las pruebas definidas en una sola clase haciendo clic con el botón derecho en la clase en la vista del proyecto y haciendo clic enRun 'StringUtilitiesTest '
o use el método abreviado de tecladoctrl+shift+f10
si ha seleccionado la clase en la vista del proyecto.Paquete (todo)
Si no desea ejecutar todas las pruebas definidas en el proyecto o en un paquete, puede simplemente hacer clic derecho en el paquete y hacer clic enRun ...
al igual que ejecutaría todas las pruebas definidas en una sola clase.
Excepciones
JUnit también se puede usar para probar si un método lanza una excepción específica para una entrada determinada.
En este ejemplo, probaremos si el siguiente método realmente produce una excepción si el formato booleano (entrada) no se reconoce / se desconoce:
public static boolean parseBoolean(@NonNull String raw) throws IllegalArgumentException{
raw = raw.toLowerCase().trim();
switch (raw) {
case "t": case "yes": case "1": case "true":
return true;
case "f": case "no": case "0": case "false":
return false;
default:
throw new IllegalArgumentException("Unknown boolean format: " + raw);
}
}
Al agregar el parámetro expected
a la anotación @Test
, se puede definir qué excepción se espera que se genere. La prueba de la unidad fallará si esta excepción no se produce, y tendrá éxito si la excepción es efectivamente lanzada:
@Test(expected = IllegalArgumentException.class)
public void parseBoolean_parsesInvalidFormat_throwsException(){
StringUtilities.parseBoolean("Hello JUnit");
}
Esto funciona bien, sin embargo, lo limita a un solo caso de prueba dentro del método. A veces es posible que desee probar varios casos dentro de un solo método. Una técnica que se usa con frecuencia para superar esta limitación es usar try-catch
bloques try-catch
y el método Assert.fail()
:
@Test
public void parseBoolean_parsesInvalidFormats_throwsException(){
try {
StringUtilities.parseBoolean("Hello!");
fail("Expected IllegalArgumentException");
} catch(IllegalArgumentException e){
}
try {
StringUtilities.parseBoolean("JUnit!");
fail("Expected IllegalArgumentException");
} catch(IllegalArgumentException e){
}
}
Nota: Algunas personas consideran que es una mala práctica probar más de un solo caso dentro de una prueba unitaria.
Importación estática
JUnit define varios métodos assertEquals
, al menos uno para cada tipo primitivo y uno para Objetos está disponible. Por defecto, estos métodos no están disponibles directamente para llamar y deben llamarse así: Assert.assertEquals
. Pero debido a que estos métodos se usan con mucha frecuencia, la gente casi siempre usa una importación estática para que el método se pueda usar directamente como si fuera parte de la clase en sí.
Para agregar una importación estática para el método assertEquals
, use la siguiente declaración de importación:
import static org.junit.Assert.assertEquals;
También puede importar de forma estática todos los métodos de assert, incluyendo assertArrayEquals
, assertNotNull
y assertFalse
etc., utilizando la siguiente importación estática:
import static org.junit.Assert.*;
Sin importación estática:
@Test
public void addition_isCorrect(){
Assert.assertEquals(4 , 2 + 2);
}
Con importación estática:
@Test
public void addition_isCorrect(){
assertEquals(4 , 2 + 2);
}