Podpora pro integrační testy

Podpora pro integrační testy

Pro integrační testy EdeeShop modulu lze použít následující třídy, které se nacházejí v knihovně lib_eshop_integration_tests:

xml
1 <dependency>2    <groupId>com.fg.cps.eshop</groupId>3    <artifactId>lib_eshop_integration_tests</artifactId>4    <version>${eshop.version}</version>5    <scope>test</scope>6</dependency>

Zkopírovat odkaz na sekciAplikační kontext

Správná funkčnost support tříd vyžaduje aplikační kontext, který bude obsahovat EdeeCms včetně EdeeShop modulu. Pro tyto účely je možné extendovat com.fg.cps.eshop.edee.AbstractEdeeShopModuleTestCase, která vytváří potřebný context na základě konfigurace uvedené v META-INF/lib_eshop_edee_test/sitemap/standardEdeeShopModuleConfig.xml.

Použití projektové konfigurace lze docílit přidáním k testu této anotace @EdeeShopFeatureUnitTestConfigFile("META-INF/lib_project/sitemap/projectSitemap.xml"). Vlastní konfigurace pak může vycházet z té standardní (standardEdeeShopModuleConfig.xml) a být jen rozšířena o projektové rozšíření. Pomocí anotace EdeeShopFeatureHostConfigurableWebContextLoader lze pak docílit načítání .properties konfigurací podle hostname v META-INF/conf/spring-populate.properties.

Předek pro projektové testy pak může vypadat např. takto:

java
1 @ContextConfiguration(2    inheritLocations = false,3    loader = EdeeShopFeatureHostConfigurableWebContextLoader.class,4    classes = { ProjectTestConfig.class } // toto vyžaduje Spring i pro prázdný config, aby nebylo u každého konkrétního testu nutné specifikovat `ContextConfiguration`5)6@EdeeShopFeatureUnitTestConfigFile("META-INF/lib_project/sitemap/projectSitemap.xml")7@Transactional8public abstract class AbstractProjectTest extends AbstractEdeeShopModuleTestCase {9
10	protected EvitaTestSupport evitaTestSupport;11	protected PriceTestSupport priceTestSupport;12	protected PublishingTestSupport publishingTestSupport;13	// and other support classes14
15	@Before16	public void before() {17		super.before();18
19		evitaTestSupport = new EvitaTestSupport(applicationContext, PRODUCT_TEMPLATE_CATALOG_CODE);20        publishingTestSupport = new PublishingTestSupport(applicationContext, Collections.singletonMap(PRODUCT_TEMPLATE_CATALOG_CODE, evitaTestSupport));21		priceTestSupport = new PriceTestSupport(publishingTestSupport, catalogService, applicationContext);22		// and other support classes23	}24
25    @After26    public void after() {27        this.centralCacheManager.clearAllCaches();28        this.evitaTestSupport.values().forEach(EvitaTestSupport::resetEvita);29    }30}
java
1 @Configuration2public class ProjectTestConfig {}

Konkrétní testovací třídy využívající běžící edeeShop pak už potřebují pouze dědit z AbstractProjectTest, případně mohou obsahovat anotaci ContextConfiguration s vlastní dodatečnou Springovou konfigurací pro daný test:

java
1 @ContextConfiguration(classes = {ProjectServiceConfig.class})2public class ProjectServiceTest extends AbstractProjectTest {3    // ... JUnit tests ... 4}

Zkopírovat odkaz na sekciTestDataGenerator - pro vygenerování zdrojových dat

java
1 final TestDataGenerator testDataGenerator = new TestDataGenerator(applicationContext);2
3// příklad vygenerování produktu4testDataGenerator.createProduct(5        new TestDataRequestProduct(TEST_CATALOG_CODE, "kodProduktu")6                .generateDefaultPrice()7                .setAvailabilty(ProductAvailability.DEPEND_ON_STOCK)8                .setStockQuantity(BigDecimal.TEN)9);10
11// příklad vygenerování dopravy12testDataGenerator.createShipping(13        new TestDataRequestShipping(code, type)14            .setStatus(status)15            .setPriceWithTax(new BigDecimal(priceWithTax)16        )17);18
19// příklad vygenerování platební metody20testDataGenerator.createPayment(21        new TestDataRequestPayment(paymentCode, paymentType)22            .setStatus(paymentStatus)23            .setPriceWithTax(new BigDecimal(priceWithTax)24        )25);

Zkopírovat odkaz na sekciPublishingTestSupport - pro vypublikování zdrojových dat

java
1 PublishingTestSupport publishingTestSupport = new PublishingTestSupport(applicationContext);2
3// příklad vygenerování produktu a jeho vypublikování4runAndPublish(() -> {5        testDataGenerator.createProduct(6                new TestDataRequestProduct(TEST_CATALOG_CODE, "kodProduktu")7                        .generateDefaultPrice()8                        .setAvailabilty(ProductAvailability.DEPEND_ON_STOCK)9                        .setStockQuantity(BigDecimal.TEN)10        );11});

Zkopírovat odkaz na sekciEvitaTestSupport - pro indexaci dat do Evity

java
1 EvitaTestSupport evitaTestSupport = new EvitaTestSupport(applicationContext, catalogCode);2evitaTestSupport.indexEvita();

Zkopírovat odkaz na sekciPriceTestSupport - pro vygenerování a vypublikování základních ceníků

java
1 // příklad vygenerováné a vypublikování základní a referenčního ceníku2priceTestSupport.getOrCreateBasePriceListsAndPublish(catalogCode)

Zkopírovat odkaz na sekciOrderTestSupport - pro vytvoření objednávky nebo košíku

java
1 OrderTestSupport orderTestSupport = new OrderTestSupport(2		publishingSupport,3		evitaTestSupport,4		testDataGenerator,5		shoppingCartService,6		metadataManager,7		orderService,8		userPriceResolver9);10
11// příklad vytvoření objednávky 12Order order = orderTestSupport.createOrder(13                new AddItemByCodeToCart(product02, new BigDecimal(3)),14                orderTestSupport.createDefaultContactInformationUpdate(),15                new SetShippingByCodeUpdate(PPL.name()),16                new SetPaymentByCodeUpdate(CASH_ON_DELIVERY.name())17);

Zkopírovat odkaz na sekciTesty a oprávnění

Při testování je nutné mít na paměti, testovaná logika může být závislá na oprávněních. Pokud není v testech oprávnění ověřovat, je možné testovací metodu označit anotací @RunAsAdmin. V případě, že je nutné ověřit oprávnění, je možné využít třídu com.fg.cps.eshop.registration.service.PermissionService. Více zde.

Zkopírovat odkaz na sekciIntegrační testování eshopových struktur na projektech

Každá z testových tříd na projektech by měla extendovat projektovou implementaci třídy AbstractProjectTest, která zajišťuje správné nastavení aplikačního kontextu a inicializaci potřebných komponent pro testování včetně vyprázdnění cache a vymazání indexovaných dat v Evitě pro zajištění čistého stavu před každým testem. Ve zmíněné projektové třídě je vhodné inicializovat často používané věci jako ObjectMapper nebo MockMvc pro testy controllerů. Zároveň, pokud je žádoucí a předpokladané, že nějaká data budou vždy potřebná, právě zde (v @Before) je možné je vygenerovat a publikovat (typickým příkladem mohou být například štítky).

Pro zajištění stejné transakce testu pro publikace je vhodné nastavit na instanci publikačního žurnálu PublishingJournalService metodu setShareTransaction(true). Tímto způsobem se zajistí, že publikace použije transakci testu a nezakládá si novou.

V konkrétních testových třídách lze přetížit zmíněné metody before() a after(), které se volají před a po každém testu - je pro zachování fuknčnosti je důležité nezapomenout provolat odpovídající metodu z předka pro nastavení kritických služeb pro testování. Důvodem pro přetížení je příprava relevantních dat pro danou testovací třídu, jako například vygenerování produktů (včetně cen a ostatních vlastností), kateogrií, paremetrů apod. Tyto struktury je možné buď vytvořit pomocí TestDataGenerator nebo je možné použít systémové služby pro jejich vytváření (např. AbstractProductService<Product>). Pokud je chtěné mít vygenerováná data i v publikovaných datech v evitaDB, je nutné logiku pro vytváření daných struktur provolat v pomocí publishingTestSupport.runAndPublishAndReindex(), viz. ukázka níže:

java
1 publishingTestSupport.runAndPublishAndReindex(2                "catalogCode",3                () -> {4                    super.before();5                    initTags();6                    initPriceLists();7                    initProductsWithPrices();8                    initMarketingCampaigns();9                }10        );

Je vhodné připomenout, že když v rámci integračních testů shopu je testována logika, která ma na základě nějaké události upravovat již fetchnutý produkt, pro ověření aplikování změny je nutné opětovně získat produkt z primárních / indexovaných dat - změny nejsou nijak propagovány do již fetchnutých strutkur. Stejně tak, pokud přímo v testovací metodě má dojít k publikaci dat (změny cen produktů, smazání produktu, změna kategorií, ...), je zde nutné provolat logiku spouštějící proces publikace dat a následnou inkrementální indexaci pomocí publishingTestSupport.runAndPublish().

Pro testování košíku lze využít OrderTestSupport.runInDefaultTestCartContext(), která slouží k vytvoření košíku nad specifikovaným katalogem po zadaného uživatele s použitím vybrané měny. Následné operace v košíku lze provádět a propagovat do kontextu tohoto košíku v lambda parametru cartContext pomocí shoppingCartService a dílčích operací dědících AbstractCartUpdate, konrétní příklad je na ukázce níže:

java
1 final Map<String, PublishedShippingMethodContract<?,?>> availableShippingOptionsForCart = orderTestSupport.runInDefaultTestCartContext(2        "catalogCode",3        "test@test.cz",4        Currency.getInstance("CZK"),5        "CZ",6        List.of(),7        cartContext -> {8            shoppingCartService.updateCart(new AddItemByCodeToCart(product1.getCode(), BigDecimal.ONE));9            shoppingCartService.updateCart(new AddItemByCodeToCart(product2.getCode(), BigDecimal.ONE));10            shoppingCartService.updateCart(createContactInformationUpdate());11            shoppingCartService.updateCart(new SetPaymentByCodeUpdate("payment01"));12            13            return shippingMethodCartProvider.getAvailableShippings(cartContext);14        }15);

Při testování je vhodné mít na paměti, v jakém katalogu se budou při reálném běhu aplikace nacházet entity, které jsou v rámci testu používány. Eshop může ve vícekatalogovém řešení rozlišovat origin katalog a z něj vycházející alias katalogy. Aplikační logika se o tento koncept opírá a staví na něm, proto je nutné v testech dodržovat stejné principy. Například produkty by v tomto případě měly být vytvářeny v origin katalogu, kde je s nimi počítáno, obdobně jako tomu může být u kategorií nebo štítků. Produkty se pak do alias katalogů dostávají přes jako 'alisy origin produktů' přes službu k tomu určenou (AbstractProductAliasService), což je také nutné vypublikovat do evitaDB. Mezi entity, které může patřit pouze do alias katalogu mohou patřit například dopravní či platební metody, které mohou existovat pouze pro daný lokalizovaný katalog. Pro při testování komplexní logiky kombinující prvky jak z origin katalogu, tak z alias katalogu je nutné mít vytvořené oba katalogy a publikované entity do správných katalogů.