Cucumber – Trabajando con contextos

Cucumber es una herramienta que soporta BDD que hemos utilizado recientemente y que ha sido muy útil para verificar los resultados de las funcionalidades de nuestro proyecto bajo ciertos escenarios usando Gherkin que es un lenguaje fácil de entender por personas no técnicas.

En este artículo creamos un ejemplo práctico para gestionar inventarios donde podemos reubicar sus elementos entre diferentes ubicaciones destino, la diferencia con nuestro otro artículo Cucumber – Ejemplo práctico es que aquí compartiremos el contexto de la prueba entre steps que se encuentran en diferentes clases.

Dependencias Maven

Agregamos las siguientes dependencias en nuestro archivo pom.xml

<dependency>
	<groupId>io.cucumber</groupId>
	<artifactId>cucumber-java</artifactId>
	<version>7.9.0</version>
</dependency>

<dependency>
	<groupId>io.cucumber</groupId>
	<artifactId>cucumber-junit</artifactId>
	<version>7.9.0</version>
</dependency>

<dependency>
	<groupId>io.cucumber</groupId>
	<artifactId>cucumber-picocontainer</artifactId>
	<version>7.9.0</version>
</dependency>

La Clase

Para la gestión del inventario creamos la clase Inventory.java, InventoryLine.java e Item.java

La clase inventory contiene los inventory lines y el método relocate que se encarga de reubicar los items existentes a una nueva ubicación.

public class Inventory {
    private Set<Item> items;
	private Set<InventoryLine> inventoryLines;

    public boolean relocate(String itemCode, Integer qty, String 
                            sourceLocation, String targetLocation) {

		var item = new Item(itemCode);

		InventoryLine line = inventoryLines
            .stream()
            .filter(l -> l.getItem().equals(item)
				&& StringUtils.equals(l.getLocation(), sourceLocation) 
                && l.getQuantity().compareTo(qty) >= 0)
			.findFirst().orElse(null);

		if (line == null) {
			return false;
		}

		if (line.getQuantity().compareTo(qty) == 0) {
			line.setLocation(targetLocation);

		} else {
			line.reduceQuantity(qty);

			var newLine = new InventoryLine();
			newLine.setItem(item);
			newLine.setLocation(targetLocation);
			newLine.setQuantity(qty);
			inventoryLines.add(newLine);
		}

		return true;
	}
}

La clase InventoryLine e Item son simples beans que representan a los objetos de la vida real.

public class Item {
    private String itemCode;
    private String sku;
    private String description;
}
public class InventoryLine {
	private Item item;
	private Integer quantity;
	private String location;
}

El Contexto

La clase TestContext permite compartir la información entre las diversas clases que definen los pasos de los escenarios, esto se hace a través del constructor de las clases que definen los pasos.

public class TestContext {

	private Inventory inventory;
}

El Escenario

Los escenarios son creados en archivos con extensión .feature y en lenguaje Gherkin como había sido mencionado anteriormente.

El siguiente escenario nos ayuda a validar los resultados esperados del inventario después de una reubicación de los items.

Se observan 3 pasos muy importantes,

Given.- describe el contexto inicial de la aplicación, en este caso el inventario inicial.

When.- describe el evento de reubicación del item del location 1 al location 5.

Then.- describe el resultado esperado.

Feature: Testing the relocation logic
  Users should be able to relocate the items of an inventory

  Scenario: Move items to other location
    Given the following items
    | item code | description | sku   |
    | item-1    | T-Shirt     | SKU-1 |
    | item-2    | Skirt       | SKU-2 |
    | item-3    | Shirt       | SKU-3 |
    | item-4    | Pants       | SKU-4 |
  	And an inventory with the following items
  	| item code | qty | location |
  	| item-1 	| 10  | loc1  |
  	| item-2 	| 20  | loc2  |
  	| item-3 	| 30  | loc3  |
  	| item-4 	| 40  | loc4  |
    When users relocate the following inventory to the location "loc5"
    | item code | qty | location |
  	| item-1 	| 10  | loc1     |
    Then the expected inventory is the following
    | item code | qty | location |
  	| item-1 	| 10  | loc5     |
  	| item-2 	| 20  | loc2     |
  	| item-3 	| 30  | loc3     |
  	| item-4 	| 40  | loc4     |

Cuando ejecutemos el escenario, cucumber buscara la implementación del step en nuestras clases para saber que lógica se va a ejecutar, por ende necesitamos definir la lógica de cada paso usando diferentes métodos que harán match con el paso haciendo uso de las anotaciones.

Para este ejemplo usamos dos clases donde se encuentran nuestras definiciones.

La clase InventorySteps que contiene la logica relacionada con el inventario.

public class InventorySteps {

	private Inventory inventory;

	public InventorySteps(TestContext context) {
		this.inventory = context.getInventory();
	}

	@And("an inventory with the following items")
	public void anInventoryWithTheFollowingItems(List<InventoryLine> inventoryLines) {
		this.inventory.setInventoryLines(new HashSet<>(inventoryLines));
	}

	@When("users relocate the following inventory to the location {string}")
	public void relocate(String targetLocation, List<InventoryLine> inventoryLines) {
		var inventoryLine = inventoryLines.get(0);
		inventory.relocate(inventoryLine.getItem().getItemCode(), inventoryLine.getQuantity(),
				inventoryLine.getLocation(), targetLocation);
	}

	@Then("the expected inventory is the following")
	public void ss(List<InventoryLine> expectedInventoryLines) {

		assertNotNull(inventory.getInventoryLines());
		assertEquals(expectedInventoryLines.size(), inventory.getInventoryLines().size());
		for (var line : inventory.getInventoryLines()) {

			var expectedLine = expectedInventoryLines.stream()
					.filter(l -> StringUtils.equals(l.getLocation(), line.getLocation())).findFirst().orElse(null);
			assertNotNull(expectedLine);

			assertEquals(expectedLine.getItem(), line.getItem());
			assertEquals(expectedLine.getQuantity(), line.getQuantity());
		}

	}

	@DataTableType
	public InventoryLine mapInventoryLine(Map<String, String> data) {

		var itemCode = data.get("item code");

		var item = this.inventory.getItems().stream().filter(i -> StringUtils.equals(i.getItemCode(), itemCode))
				.findFirst().orElseThrow();

		var line = new InventoryLine();
		line.setItem(item);
		line.setQuantity(Integer.valueOf(data.get("qty")));
		line.setLocation(data.get("location"));
		return line;
	}

}

La clase ItemSteps que contiene la lógica relacionada con la carga de items.

public class ItemSteps {
	
	private Inventory inventory;
	
	public ItemSteps(TestContext context) {
		this.inventory = context.getInventory();
	}
	
	@Given("the following items")
	public void theFollowingItems(List<Item> items) {
		this.inventory.setItems(new HashSet<>(items));
	}
	
	@DataTableType
	public Item mapItems(Map<String, String> data) {
		var item = new Item();
		item.setItemCode(data.get("item code"));
		item.setDescription(data.get("description"));
		item.setSku(data.get("sku"));
		return item;
	}

}

Como se observa en las clases de arriba, estas cuentan con constructores que reciben la clase TestContext.java

Ejecutamos los escenarios y obtenemos la siguiente salida en la consola, que como se observa las pruebas fueron ejecutadas satisfactoriamente.

[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running com.indevsolutions.example.cucumber.context.CucumberIntegrationTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.792 s - in com.indevsolutions.example.cucumber.context.CucumberIntegrationTest
[INFO] 
[INFO] Results:
[INFO] 
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  12.539 s
[INFO] Finished at: 2022-12-18T01:59:08-05:00
[INFO] ------------------------------------------------------------------------

Como se observa, el uso de cucumber puede ser de mucha ayuda y al ser entendible por personas no técnicas podemos involucrar al equipo del producto para realizar las validaciones y hasta crear más escenarios.

Como siempre puedes encontrar el código del ejemplo aquí.

Con esto nos despedimos y esperamos que hayamos podido contribuir en algo compartiendo nuestro conocimiento.

Leave a comment

Tu dirección de correo electrónico no será publicada.

slot gacorslot gacorMurah4dtotoslot88Horas88Slot PulsaSlot DanaSpboNowSkorWomen In The WorldSlot88rubiesandradishesorganicnewsroomvirante seocloud spectatorVainglory Forumamerican gathering of jewish holocaust survivorsRumah Pejuang Kanker Ambuoutside foundWetpaintcwhonorsSlot GacorJudi BolaDemo SlotDemo SlotSlot GacorSlot GacorJudi BolaJudi Bolaomo77omo77omo777omo777UnogoalSPBOATLASBET88CUANWIN138MACANASIA88https://direct.lc.chat/14614518/https://secure.livechatinc.com/licence/14614518/v2/open_chat.cgihttps://direct.lc.chat/15014550/https://secure.livechatinc.com/licence/15014550/v2/open_chat.cgihttps://direct.lc.chat/15314022/https://secure.livechatinc.com/licence/15314022/v2/open_chat.cgihttps://ibrapchs.net/https://atox.in/
https://slot77.amikmahaputra.ac.id/products/slot77/https://gacor4d.amikmahaputra.ac.id/products/gacor4d/https://infini88.amikmahaputra.ac.id/products/infini88/https://sv388.amikmahaputra.ac.id/products/sv388/https://pay4d.amikmahaputra.ac.id/products/pay4d/https://parlay-bola.amikmahaputra.ac.id/products/mix-parlay/https://idn-slot.amikmahaputra.ac.id/products/idn-slot/https://mpo-slot.amikmahaputra.ac.id/products/mpo-slot/https://nexus-slot.amikmahaputra.ac.id/products/nexus-slot/https://mahjong-ways.amikmahaputra.ac.id/products/slot-mahjong/https://akun-pro-jepang.amikmahaputra.ac.id/products/slot-jepang/https://akun-pro-kamboja.amikmahaputra.ac.id/products/slot-kamboja/https://akun-pro-malaysia.amikmahaputra.ac.id/products/slot-malaysia/https://akun-pro-myanmar.amikmahaputra.ac.id/products/slot-myanmar/https://akun-pro-rusia.amikmahaputra.ac.id/products/slot-rusia/https://akun-pro-singapore.amikmahaputra.ac.id/products/slot-singapore/https://akun-pro-taiwan.amikmahaputra.ac.id/products/slot-taiwan/https://akun-pro-thailand.amikmahaputra.ac.id/products/slot-thailand/https://akun-pro-thailand.amikmahaputra.ac.id/products/slot-thailand/https://akun-pro-vietnam.amikmahaputra.ac.id/products/slot-vietnam/
https://slot-bonus.amikmahaputra.ac.id/products/slot-bonus/https://slot-demo.amikmahaputra.ac.id/products/slot-demo/https://slot-dana.amikmahaputra.ac.id/products/slot-dana/https://slot-depo-10k.amikmahaputra.ac.id/products/slot-10k/https://slot-depo-25k.amikmahaputra.ac.id/products/slot-25k/https://slot-garansi-kekalahan.amikmahaputra.ac.id/products/slot-garansi-kekalahan/https://slot-habanero.amikmahaputra.ac.id/products/slot-habanero/https://slot-hoki.amikmahaputra.ac.id/products/slot-hoki/https://slot-jp.amikmahaputra.ac.id/products/slot-jp/https://slot-maxwin.amikmahaputra.ac.id/products/slot-maxwin/https://bocoran-admin-slot.amikmahaputra.ac.id/products/bocoran-admin-slot/https://casino-online.amikmahaputra.ac.id/products/casino-online/https://bet88.amikmahaputra.ac.id/products/bet88/https://bandarqq.amikmahaputra.ac.id/products/bandarqq/https://baccarat-online.amikmahaputra.ac.id/products/baccarat-online/https://asia88.amikmahaputra.ac.id/products/asia88/https://asia4d.amikmahaputra.ac.id/products/asia4d/https://anti-rungkad.amikmahaputra.ac.id/products/anti-rungkad/https://akun-vip.amikmahaputra.ac.id/products/akun-vip/https://akun-gacor.amikmahaputra.ac.id/products/akun-gacor/
https://slot-receh.journal.poltekanika.ac.id/products/slot-receh/https://slot-pulsa.journal.poltekanika.ac.id/products/slot-depo-pulsa/https://slot-maxwin.journal.poltekanika.ac.id/products/slot-maxwin/https://slot-hoki.journal.poltekanika.ac.id/products/slot-hoki/https://slot-garansi-kekalahan.journal.poltekanika.ac.id/products/slot-garansi-kekalahan/https://slot-depo-25k.journal.poltekanika.ac.id/products/slot-25k/https://slot-depo-10k.journal.poltekanika.ac.id/products/slot-10k/https://slot-demo.journal.poltekanika.ac.id/products/slot-demo/https://slot-dana.journal.poltekanika.ac.id/products/slot-depo-dana/https://slot-bonus.journal.poltekanika.ac.id/products/bonus-new-member/
https://www.angkolabarat.tapselkab.go.id/products/slot-zeus/https://www.angkolabarat.tapselkab.go.id/products/slot-10k/https://www.angkolabarat.tapselkab.go.id/products/slot-25k/https://www.angkolabarat.tapselkab.go.id/products/slot-garansi-kekalahan/https://www.angkolabarat.tapselkab.go.id/products/slot-hoki/https://www.angkolabarat.tapselkab.go.id/products/slot-maxwin/https://www.angkolabarat.tapselkab.go.id/products/slot-pulsa/https://www.angkolabarat.tapselkab.go.id/products/slot-receh/https://www.angkolabarat.tapselkab.go.id/products/slot-toto/https://www.angkolabarat.tapselkab.go.id/products/slot-demo/>
https://www.jurnal.poltekanika.ac.id/products/asia4d/https://www.jurnal.poltekanika.ac.id/products/bet88/https://www.jurnal.poltekanika.ac.id/products/idn-slot/https://www.jurnal.poltekanika.ac.id/products/mpo-slot/https://www.jurnal.poltekanika.ac.id/products/nexus-slot/https://www.jurnal.poltekanika.ac.id/products/pay4d/https://www.jurnal.poltekanika.ac.id/products/sbobet88/https://www.jurnal.poltekanika.ac.id/products/slot77/https://www.jurnal.poltekanika.ac.id/products/slot88/https://www.jurnal.poltekanika.ac.id/products/sv388/
https://slot-bonus.pn-subang.go.id/products/bonus-new-member/https://slot-dana.pn-subang.go.id/products/slot-dana/https://slot-demo.pn-subang.go.id/products/slot-demo/https://slot-garansi-kekalahan.pn-subang.go.id/products/slot-garansi-kekalahan/https://slot-hoki.pn-subang.go.id/products/slot-hoki/https://slot-maxwin.pn-subang.go.id/products/slot-maxwin/https://slot-pulsa.pn-subang.go.id/products/slot-pulsa/https://slot-receh.pn-subang.go.id/products/slot-receh/https://slot-toto.pn-subang.go.id/products/slot-toto/https://slot-zeus.pn-subang.go.id/products/slot-zeus/https://unogoal.pn-subang.go.id/products/unogoal/https://toto-togel.pn-subang.go.id/products/toto-togel/https://spbo.pn-subang.go.id/products/spbo/https://pkv-games.pn-subang.go.id/products/pkv-games/https://parlay-bola.pn-subang.go.id/products/parlay-bola/https://nowgoal.pn-subang.go.id/products/nowgoal/https://judi-bola.pn-subang.go.id/products/judi-bola/https://idn-poker.pn-subang.go.id/products/idn-poker/https://dominoqq.pn-subang.go.id/products/dominoqq/https://bet88.pn-subang.go.id/products/bet88/
https://ppid.dpubinmarcipka.jatengprov.go.id/dokumen/link-gacor/https://ppid.dpubinmarcipka.jatengprov.go.id/dokumen/link-demo/https://ppid.dpubinmarcipka.jatengprov.go.id/dokumen/situs-thailand/https://ppid.dpubinmarcipka.jatengprov.go.id/dokumen/situs-kamboja/https://ppid.dpubinmarcipka.jatengprov.go.id/dokumen/sbobet/https://sdi.babelprov.go.id/publikasi/link-demo/https://sdi.babelprov.go.id/publikasi/situs-thailand/https://sdi.babelprov.go.id/publikasi/situs-kamboja/https://sdi.babelprov.go.id/publikasi/sbobet88/https://akun-pro-hongkong.amikmahaputra.ac.id/products/slot-hongkong/