(requestHeaders), String.class);
+ assertThat(response.getStatusCode()
+ .value()).isEqualTo(401);
+ }
+
+ private String getLanguageUrl() {
+ return "http://localhost:" + port + "/user/language";
+ }
+
+ private String getLogoutUrl() {
+ return "http://localhost:" + port + "/user/logout";
+ }
+
+}
diff --git a/spring-security-modules/spring-security-mvc-boot-2/src/test/resources/customlogouthandler/after.sql b/spring-security-modules/spring-security-mvc-boot-2/src/test/resources/customlogouthandler/after.sql
new file mode 100644
index 0000000000..df6f312987
--- /dev/null
+++ b/spring-security-modules/spring-security-mvc-boot-2/src/test/resources/customlogouthandler/after.sql
@@ -0,0 +1 @@
+delete from users;
\ No newline at end of file
diff --git a/spring-security-modules/spring-security-mvc-boot-2/src/test/resources/customlogouthandler/application.properties b/spring-security-modules/spring-security-mvc-boot-2/src/test/resources/customlogouthandler/application.properties
new file mode 100644
index 0000000000..9edd853f2c
--- /dev/null
+++ b/spring-security-modules/spring-security-mvc-boot-2/src/test/resources/customlogouthandler/application.properties
@@ -0,0 +1,5 @@
+spring.datasource.url=jdbc:postgresql://localhost:5432/test
+spring.datasource.username=test
+spring.datasource.password=test
+
+spring.jpa.hibernate.ddl-auto=create
diff --git a/spring-security-modules/spring-security-mvc-boot-2/src/test/resources/customlogouthandler/before.sql b/spring-security-modules/spring-security-mvc-boot-2/src/test/resources/customlogouthandler/before.sql
new file mode 100644
index 0000000000..bb0a85f613
--- /dev/null
+++ b/spring-security-modules/spring-security-mvc-boot-2/src/test/resources/customlogouthandler/before.sql
@@ -0,0 +1 @@
+insert into users (login, password, role, language) values ('user', '{noop}pass', 'ROLE_USER', 'english');
\ No newline at end of file
diff --git a/spring-soap/pom.xml b/spring-soap/pom.xml
index 9403b70636..d0987329c0 100644
--- a/spring-soap/pom.xml
+++ b/spring-soap/pom.xml
@@ -58,6 +58,28 @@
+
+
+ org.jvnet.jaxb2.maven2
+ maven-jaxb2-plugin
+ 0.14.0
+
+
+
+ generate
+
+
+
+
+ WSDL
+ ${project.basedir}/src/main/java
+ com.baeldung.springsoap.client.gen
+ ${project.basedir}/src/main/resources
+
+ countries.wsdl
+
+
+
diff --git a/spring-soap/src/main/java/com/baeldung/springsoap/client/CountryClient.java b/spring-soap/src/main/java/com/baeldung/springsoap/client/CountryClient.java
new file mode 100644
index 0000000000..e0b9172ece
--- /dev/null
+++ b/spring-soap/src/main/java/com/baeldung/springsoap/client/CountryClient.java
@@ -0,0 +1,26 @@
+package com.baeldung.springsoap.client;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.ws.client.core.support.WebServiceGatewaySupport;
+
+import com.baeldung.springsoap.client.gen.GetCountryRequest;
+import com.baeldung.springsoap.client.gen.GetCountryResponse;
+
+public class CountryClient extends WebServiceGatewaySupport {
+
+ private static final Logger logger = LoggerFactory.getLogger(CountryClient.class);
+
+ public GetCountryResponse getCountry(String country) {
+
+ GetCountryRequest request = new GetCountryRequest();
+ request.setName(country);
+
+ logger.info("Requesting information for " + country);
+
+ GetCountryResponse response = (GetCountryResponse) getWebServiceTemplate().marshalSendAndReceive(request);
+
+ return response;
+ }
+
+}
\ No newline at end of file
diff --git a/spring-soap/src/main/java/com/baeldung/springsoap/client/CountryClientConfig.java b/spring-soap/src/main/java/com/baeldung/springsoap/client/CountryClientConfig.java
new file mode 100644
index 0000000000..8dabdbc00d
--- /dev/null
+++ b/spring-soap/src/main/java/com/baeldung/springsoap/client/CountryClientConfig.java
@@ -0,0 +1,25 @@
+package com.baeldung.springsoap.client;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.oxm.jaxb.Jaxb2Marshaller;
+
+@Configuration
+public class CountryClientConfig {
+
+ @Bean
+ public Jaxb2Marshaller marshaller() {
+ Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
+ marshaller.setContextPath("com.baeldung.springsoap.client.gen");
+ return marshaller;
+ }
+
+ @Bean
+ public CountryClient countryClient(Jaxb2Marshaller marshaller) {
+ CountryClient client = new CountryClient();
+ client.setDefaultUri("http://localhost:8080/ws");
+ client.setMarshaller(marshaller);
+ client.setUnmarshaller(marshaller);
+ return client;
+ }
+}
\ No newline at end of file
diff --git a/spring-soap/src/main/java/com/baeldung/springsoap/client/gen/Country.java b/spring-soap/src/main/java/com/baeldung/springsoap/client/gen/Country.java
new file mode 100644
index 0000000000..e17dce55f9
--- /dev/null
+++ b/spring-soap/src/main/java/com/baeldung/springsoap/client/gen/Country.java
@@ -0,0 +1,146 @@
+//
+// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.3.0
+// See https://javaee.github.io/jaxb-v2/
+// Any modifications to this file will be lost upon recompilation of the source schema.
+// Generated on: 2020.04.25 at 03:18:49 PM IST
+//
+
+
+package com.baeldung.springsoap.client.gen;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlSchemaType;
+import javax.xml.bind.annotation.XmlType;
+
+
+/**
+ * Java class for country complex type.
+ *
+ *
The following schema fragment specifies the expected content contained within this class.
+ *
+ *
+ * <complexType name="country">
+ * <complexContent>
+ * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ * <sequence>
+ * <element name="name" type="{http://www.w3.org/2001/XMLSchema}string"/>
+ * <element name="population" type="{http://www.w3.org/2001/XMLSchema}int"/>
+ * <element name="capital" type="{http://www.w3.org/2001/XMLSchema}string"/>
+ * <element name="currency" type="{http://www.baeldung.com/springsoap/gen}currency"/>
+ * </sequence>
+ * </restriction>
+ * </complexContent>
+ * </complexType>
+ *
+ *
+ *
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name = "country", propOrder = {
+ "name",
+ "population",
+ "capital",
+ "currency"
+})
+public class Country {
+
+ @XmlElement(required = true)
+ protected String name;
+ protected int population;
+ @XmlElement(required = true)
+ protected String capital;
+ @XmlElement(required = true)
+ @XmlSchemaType(name = "string")
+ protected Currency currency;
+
+ /**
+ * Gets the value of the name property.
+ *
+ * @return
+ * possible object is
+ * {@link String }
+ *
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets the value of the name property.
+ *
+ * @param value
+ * allowed object is
+ * {@link String }
+ *
+ */
+ public void setName(String value) {
+ this.name = value;
+ }
+
+ /**
+ * Gets the value of the population property.
+ *
+ */
+ public int getPopulation() {
+ return population;
+ }
+
+ /**
+ * Sets the value of the population property.
+ *
+ */
+ public void setPopulation(int value) {
+ this.population = value;
+ }
+
+ /**
+ * Gets the value of the capital property.
+ *
+ * @return
+ * possible object is
+ * {@link String }
+ *
+ */
+ public String getCapital() {
+ return capital;
+ }
+
+ /**
+ * Sets the value of the capital property.
+ *
+ * @param value
+ * allowed object is
+ * {@link String }
+ *
+ */
+ public void setCapital(String value) {
+ this.capital = value;
+ }
+
+ /**
+ * Gets the value of the currency property.
+ *
+ * @return
+ * possible object is
+ * {@link Currency }
+ *
+ */
+ public Currency getCurrency() {
+ return currency;
+ }
+
+ /**
+ * Sets the value of the currency property.
+ *
+ * @param value
+ * allowed object is
+ * {@link Currency }
+ *
+ */
+ public void setCurrency(Currency value) {
+ this.currency = value;
+ }
+
+}
diff --git a/spring-soap/src/main/java/com/baeldung/springsoap/client/gen/Currency.java b/spring-soap/src/main/java/com/baeldung/springsoap/client/gen/Currency.java
new file mode 100644
index 0000000000..12fdef58c2
--- /dev/null
+++ b/spring-soap/src/main/java/com/baeldung/springsoap/client/gen/Currency.java
@@ -0,0 +1,47 @@
+//
+// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.3.0
+// See https://javaee.github.io/jaxb-v2/
+// Any modifications to this file will be lost upon recompilation of the source schema.
+// Generated on: 2020.04.25 at 03:18:49 PM IST
+//
+
+
+package com.baeldung.springsoap.client.gen;
+
+import javax.xml.bind.annotation.XmlEnum;
+import javax.xml.bind.annotation.XmlType;
+
+
+/**
+ * Java class for currency.
+ *
+ *
The following schema fragment specifies the expected content contained within this class.
+ *
+ *
+ * <simpleType name="currency">
+ * <restriction base="{http://www.w3.org/2001/XMLSchema}string">
+ * <enumeration value="GBP"/>
+ * <enumeration value="EUR"/>
+ * <enumeration value="PLN"/>
+ * </restriction>
+ * </simpleType>
+ *
+ *
+ */
+@XmlType(name = "currency")
+@XmlEnum
+public enum Currency {
+
+ GBP,
+ EUR,
+ PLN;
+
+ public String value() {
+ return name();
+ }
+
+ public static Currency fromValue(String v) {
+ return valueOf(v);
+ }
+
+}
diff --git a/spring-soap/src/main/java/com/baeldung/springsoap/client/gen/GetCountryRequest.java b/spring-soap/src/main/java/com/baeldung/springsoap/client/gen/GetCountryRequest.java
new file mode 100644
index 0000000000..5739ee3b96
--- /dev/null
+++ b/spring-soap/src/main/java/com/baeldung/springsoap/client/gen/GetCountryRequest.java
@@ -0,0 +1,71 @@
+//
+// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.3.0
+// See https://javaee.github.io/jaxb-v2/
+// Any modifications to this file will be lost upon recompilation of the source schema.
+// Generated on: 2020.04.25 at 03:18:49 PM IST
+//
+
+
+package com.baeldung.springsoap.client.gen;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+
+
+/**
+ * Java class for anonymous complex type.
+ *
+ *
The following schema fragment specifies the expected content contained within this class.
+ *
+ *
+ * <complexType>
+ * <complexContent>
+ * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ * <sequence>
+ * <element name="name" type="{http://www.w3.org/2001/XMLSchema}string"/>
+ * </sequence>
+ * </restriction>
+ * </complexContent>
+ * </complexType>
+ *
+ *
+ *
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name = "", propOrder = {
+ "name"
+})
+@XmlRootElement(name = "getCountryRequest")
+public class GetCountryRequest {
+
+ @XmlElement(required = true)
+ protected String name;
+
+ /**
+ * Gets the value of the name property.
+ *
+ * @return
+ * possible object is
+ * {@link String }
+ *
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets the value of the name property.
+ *
+ * @param value
+ * allowed object is
+ * {@link String }
+ *
+ */
+ public void setName(String value) {
+ this.name = value;
+ }
+
+}
diff --git a/spring-soap/src/main/java/com/baeldung/springsoap/client/gen/GetCountryResponse.java b/spring-soap/src/main/java/com/baeldung/springsoap/client/gen/GetCountryResponse.java
new file mode 100644
index 0000000000..ba1ab56cf8
--- /dev/null
+++ b/spring-soap/src/main/java/com/baeldung/springsoap/client/gen/GetCountryResponse.java
@@ -0,0 +1,71 @@
+//
+// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.3.0
+// See https://javaee.github.io/jaxb-v2/
+// Any modifications to this file will be lost upon recompilation of the source schema.
+// Generated on: 2020.04.25 at 03:18:49 PM IST
+//
+
+
+package com.baeldung.springsoap.client.gen;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlType;
+
+
+/**
+ * Java class for anonymous complex type.
+ *
+ *
The following schema fragment specifies the expected content contained within this class.
+ *
+ *
+ * <complexType>
+ * <complexContent>
+ * <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
+ * <sequence>
+ * <element name="country" type="{http://www.baeldung.com/springsoap/gen}country"/>
+ * </sequence>
+ * </restriction>
+ * </complexContent>
+ * </complexType>
+ *
+ *
+ *
+ */
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name = "", propOrder = {
+ "country"
+})
+@XmlRootElement(name = "getCountryResponse")
+public class GetCountryResponse {
+
+ @XmlElement(required = true)
+ protected Country country;
+
+ /**
+ * Gets the value of the country property.
+ *
+ * @return
+ * possible object is
+ * {@link Country }
+ *
+ */
+ public Country getCountry() {
+ return country;
+ }
+
+ /**
+ * Sets the value of the country property.
+ *
+ * @param value
+ * allowed object is
+ * {@link Country }
+ *
+ */
+ public void setCountry(Country value) {
+ this.country = value;
+ }
+
+}
diff --git a/spring-soap/src/main/java/com/baeldung/springsoap/client/gen/ObjectFactory.java b/spring-soap/src/main/java/com/baeldung/springsoap/client/gen/ObjectFactory.java
new file mode 100644
index 0000000000..88b27245be
--- /dev/null
+++ b/spring-soap/src/main/java/com/baeldung/springsoap/client/gen/ObjectFactory.java
@@ -0,0 +1,63 @@
+//
+// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.3.0
+// See https://javaee.github.io/jaxb-v2/
+// Any modifications to this file will be lost upon recompilation of the source schema.
+// Generated on: 2020.04.25 at 03:18:49 PM IST
+//
+
+
+package com.baeldung.springsoap.client.gen;
+
+import javax.xml.bind.annotation.XmlRegistry;
+
+
+/**
+ * This object contains factory methods for each
+ * Java content interface and Java element interface
+ * generated in the com.baeldung.springsoap.client.gen package.
+ * An ObjectFactory allows you to programatically
+ * construct new instances of the Java representation
+ * for XML content. The Java representation of XML
+ * content can consist of schema derived interfaces
+ * and classes representing the binding of schema
+ * type definitions, element declarations and model
+ * groups. Factory methods for each of these are
+ * provided in this class.
+ *
+ */
+@XmlRegistry
+public class ObjectFactory {
+
+
+ /**
+ * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: com.baeldung.springsoap.client.gen
+ *
+ */
+ public ObjectFactory() {
+ }
+
+ /**
+ * Create an instance of {@link GetCountryRequest }
+ *
+ */
+ public GetCountryRequest createGetCountryRequest() {
+ return new GetCountryRequest();
+ }
+
+ /**
+ * Create an instance of {@link GetCountryResponse }
+ *
+ */
+ public GetCountryResponse createGetCountryResponse() {
+ return new GetCountryResponse();
+ }
+
+ /**
+ * Create an instance of {@link Country }
+ *
+ */
+ public Country createCountry() {
+ return new Country();
+ }
+
+}
diff --git a/spring-soap/src/main/java/com/baeldung/springsoap/client/gen/package-info.java b/spring-soap/src/main/java/com/baeldung/springsoap/client/gen/package-info.java
new file mode 100644
index 0000000000..eefed169a8
--- /dev/null
+++ b/spring-soap/src/main/java/com/baeldung/springsoap/client/gen/package-info.java
@@ -0,0 +1,9 @@
+//
+// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.3.0
+// See https://javaee.github.io/jaxb-v2/
+// Any modifications to this file will be lost upon recompilation of the source schema.
+// Generated on: 2020.04.25 at 03:18:49 PM IST
+//
+
+@javax.xml.bind.annotation.XmlSchema(namespace = "http://www.baeldung.com/springsoap/gen", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)
+package com.baeldung.springsoap.client.gen;
diff --git a/spring-soap/src/main/resources/countries.wsdl b/spring-soap/src/main/resources/countries.wsdl
new file mode 100644
index 0000000000..2a2eaf05bb
--- /dev/null
+++ b/spring-soap/src/main/resources/countries.wsdl
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-soap/src/test/java/com/baeldung/springsoap/client/CountryClientLiveTest.java b/spring-soap/src/test/java/com/baeldung/springsoap/client/CountryClientLiveTest.java
new file mode 100644
index 0000000000..63dbe2a5cf
--- /dev/null
+++ b/spring-soap/src/test/java/com/baeldung/springsoap/client/CountryClientLiveTest.java
@@ -0,0 +1,36 @@
+package com.baeldung.springsoap.client;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.support.AnnotationConfigContextLoader;
+
+import com.baeldung.springsoap.client.gen.Currency;
+import com.baeldung.springsoap.client.gen.GetCountryResponse;
+
+//Ensure that the server - com.baeldung.springsoap.Application - is running before executing this test
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration(classes = CountryClientConfig.class, loader = AnnotationConfigContextLoader.class)
+public class CountryClientLiveTest {
+
+ @Autowired
+ CountryClient client;
+
+ @Test
+ public void givenCountryService_whenCountryPoland_thenCapitalIsWarsaw() {
+ GetCountryResponse response = client.getCountry("Poland");
+ assertEquals("Warsaw", response.getCountry()
+ .getCapital());
+ }
+
+ @Test
+ public void givenCountryService_whenCountrySpain_thenCurrencyEUR() {
+ GetCountryResponse response = client.getCountry("Spain");
+ assertEquals(Currency.EUR, response.getCountry()
+ .getCurrency());
+ }
+}
diff --git a/testing-modules/mockito-2/README.md b/testing-modules/mockito-2/README.md
index 064366dfd5..6c9ddee01d 100644
--- a/testing-modules/mockito-2/README.md
+++ b/testing-modules/mockito-2/README.md
@@ -4,3 +4,4 @@
- [Lazy Verification with Mockito 2](https://www.baeldung.com/mockito-2-lazy-verification)
- [Mockito Strict Stubbing and The UnnecessaryStubbingException](https://www.baeldung.com/mockito-unnecessary-stubbing-exception)
- [Mockito and Fluent APIs](https://www.baeldung.com/mockito-fluent-apis)
+- [Mocking the ObjectMapper readValue() Method](https://www.baeldung.com/mockito-mock-jackson-read-value)
diff --git a/testing-modules/mockito-2/pom.xml b/testing-modules/mockito-2/pom.xml
index 76608a3039..340af89c82 100644
--- a/testing-modules/mockito-2/pom.xml
+++ b/testing-modules/mockito-2/pom.xml
@@ -14,8 +14,23 @@
../../
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ ${jackson.version}
+
+
+ org.mockito
+ mockito-junit-jupiter
+ ${mockito.version}
+ test
+
+
+
2.21.0
+ 2.10.3
diff --git a/testing-modules/mockito-2/src/main/java/com/baeldung/mockito/objectmapper/Flower.java b/testing-modules/mockito-2/src/main/java/com/baeldung/mockito/objectmapper/Flower.java
new file mode 100644
index 0000000000..bc366a39f9
--- /dev/null
+++ b/testing-modules/mockito-2/src/main/java/com/baeldung/mockito/objectmapper/Flower.java
@@ -0,0 +1,32 @@
+package com.baeldung.mockito.objectmapper;
+
+public class Flower {
+
+ private String name;
+ private Integer petals;
+
+ public Flower(String name, Integer petals) {
+ this.name = name;
+ this.petals = petals;
+ }
+
+ public Flower() {
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Integer getPetals() {
+ return petals;
+ }
+
+ public void setPetals(Integer petals) {
+ this.petals = petals;
+ }
+
+}
diff --git a/testing-modules/mockito-2/src/main/java/com/baeldung/mockito/objectmapper/FlowerJsonStringValidator.java b/testing-modules/mockito-2/src/main/java/com/baeldung/mockito/objectmapper/FlowerJsonStringValidator.java
new file mode 100644
index 0000000000..91bad66e6d
--- /dev/null
+++ b/testing-modules/mockito-2/src/main/java/com/baeldung/mockito/objectmapper/FlowerJsonStringValidator.java
@@ -0,0 +1,17 @@
+package com.baeldung.mockito.objectmapper;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class FlowerJsonStringValidator {
+ private ObjectMapper objectMapper;
+
+ public FlowerJsonStringValidator(ObjectMapper objectMapper) {
+ this.objectMapper = objectMapper;
+ }
+
+ public boolean flowerHasPetals(String jsonFlowerAsString) throws JsonProcessingException {
+ Flower flower = objectMapper.readValue(jsonFlowerAsString, Flower.class);
+ return flower.getPetals() > 0;
+ }
+}
diff --git a/testing-modules/mockito-2/src/test/java/com/baeldung/mockito/objectmapper/FlowerJsonStringValidatorUnitTest.java b/testing-modules/mockito-2/src/test/java/com/baeldung/mockito/objectmapper/FlowerJsonStringValidatorUnitTest.java
new file mode 100644
index 0000000000..31c3f0d01d
--- /dev/null
+++ b/testing-modules/mockito-2/src/test/java/com/baeldung/mockito/objectmapper/FlowerJsonStringValidatorUnitTest.java
@@ -0,0 +1,53 @@
+package com.baeldung.mockito.objectmapper;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@ExtendWith(MockitoExtension.class)
+public class FlowerJsonStringValidatorUnitTest {
+
+ @Mock
+ private ObjectMapper objectMapper;
+
+ private FlowerJsonStringValidator flowerJsonStringValidator;
+
+ @BeforeEach
+ public void setUp() {
+ flowerJsonStringValidator = new FlowerJsonStringValidator(objectMapper);
+ }
+
+ @Test
+ public void whenCallingHasPetalsWithPetals_thenReturnsTrue() throws JsonProcessingException {
+ Flower rose = new Flower("testFlower", 100);
+
+ when(objectMapper.readValue(anyString(), eq(Flower.class))).thenReturn(rose);
+
+ assertTrue(flowerJsonStringValidator.flowerHasPetals("this can be a very long json flower"));
+
+ verify(objectMapper, times(1)).readValue(anyString(), eq(Flower.class));
+ }
+
+ @Test
+ public void whenCallingHasPetalsWithZeroPetal_thenReturnsFalse() throws JsonProcessingException {
+ Flower rose = new Flower("testFlowerWithoutPetal", 0);
+
+ when(objectMapper.readValue(anyString(), eq(Flower.class))).thenReturn(rose);
+
+ assertFalse(flowerJsonStringValidator.flowerHasPetals("this can be a very long json flower"));
+
+ verify(objectMapper, times(1)).readValue(anyString(), eq(Flower.class));
+ }
+}
diff --git a/testing-modules/pom.xml b/testing-modules/pom.xml
index 951909b36f..b467b3c503 100644
--- a/testing-modules/pom.xml
+++ b/testing-modules/pom.xml
@@ -33,6 +33,7 @@
selenium-junit-testng
spring-testing
test-containers
+ testing-assertions
testng
junit-5-basics
easymock
diff --git a/testing-modules/testing-assertions/pom.xml b/testing-modules/testing-assertions/pom.xml
new file mode 100644
index 0000000000..0a7c4b0860
--- /dev/null
+++ b/testing-modules/testing-assertions/pom.xml
@@ -0,0 +1,28 @@
+
+ 4.0.0
+ testing-assertions
+ 0.0.1-SNAPSHOT
+
+
+ com.baeldung
+ parent-java
+ 0.0.1-SNAPSHOT
+ ../../parent-java
+
+
+
+
+ ch.qos.logback
+ logback-classic
+ 1.2.3
+
+
+ org.assertj
+ assertj-core
+ 3.15.0
+ test
+
+
+
diff --git a/testing-modules/testing-assertions/src/main/java/com/baeldung/junit/log/BusinessWorker.java b/testing-modules/testing-assertions/src/main/java/com/baeldung/junit/log/BusinessWorker.java
new file mode 100644
index 0000000000..86cd38824c
--- /dev/null
+++ b/testing-modules/testing-assertions/src/main/java/com/baeldung/junit/log/BusinessWorker.java
@@ -0,0 +1,16 @@
+package com.baeldung.junit.log;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class BusinessWorker {
+ private static Logger LOGGER = LoggerFactory.getLogger(BusinessWorker.class);
+
+ public void generateLogs(String msg) {
+ LOGGER.trace(msg);
+ LOGGER.debug(msg);
+ LOGGER.info(msg);
+ LOGGER.warn(msg);
+ LOGGER.error(msg);
+ }
+}
diff --git a/testing-modules/testing-assertions/src/main/resources/logback.xml b/testing-modules/testing-assertions/src/main/resources/logback.xml
new file mode 100644
index 0000000000..d485da62ff
--- /dev/null
+++ b/testing-modules/testing-assertions/src/main/resources/logback.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+ %d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/testing-modules/testing-assertions/src/test/java/com/baeldung/junit/log/BusinessWorkerUnitTest.java b/testing-modules/testing-assertions/src/test/java/com/baeldung/junit/log/BusinessWorkerUnitTest.java
new file mode 100644
index 0000000000..9200caf13d
--- /dev/null
+++ b/testing-modules/testing-assertions/src/test/java/com/baeldung/junit/log/BusinessWorkerUnitTest.java
@@ -0,0 +1,48 @@
+package com.baeldung.junit.log;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.slf4j.LoggerFactory;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.LoggerContext;
+
+public class BusinessWorkerUnitTest {
+ private static MemoryAppender memoryAppender;
+ private static final String LOGGER_NAME = "com.baeldung.junit.log";
+ private static final String MSG = "This is a test message!!!";
+
+ @Before
+ public void setup() {
+ Logger logger = (Logger) LoggerFactory.getLogger(LOGGER_NAME);
+ memoryAppender = new MemoryAppender();
+ memoryAppender.setContext((LoggerContext) LoggerFactory.getILoggerFactory());
+ logger.setLevel(Level.DEBUG);
+ logger.addAppender(memoryAppender);
+ memoryAppender.start();
+
+ }
+
+ @After
+ public void cleanUp() {
+ memoryAppender.reset();
+ memoryAppender.stop();
+ }
+
+ @Test
+ public void test() {
+ BusinessWorker worker = new BusinessWorker();
+ worker.generateLogs(MSG);
+
+ // I check that I only have 4 messages (all but trace)
+ assertThat(memoryAppender.countEventsForLogger(LOGGER_NAME)).isEqualTo(4);
+ // I look for a specific message at a specific level, and I only have 1
+ assertThat(memoryAppender.search(MSG, Level.INFO).size()).isEqualTo(1);
+ // I check that the entry that is not present is the trace level
+ assertThat(memoryAppender.contains(MSG, Level.TRACE)).isFalse();
+ }
+}
diff --git a/testing-modules/testing-assertions/src/test/java/com/baeldung/junit/log/MemoryAppender.java b/testing-modules/testing-assertions/src/test/java/com/baeldung/junit/log/MemoryAppender.java
new file mode 100644
index 0000000000..31c5c69766
--- /dev/null
+++ b/testing-modules/testing-assertions/src/test/java/com/baeldung/junit/log/MemoryAppender.java
@@ -0,0 +1,51 @@
+package com.baeldung.junit.log;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.read.ListAppender;
+
+/**
+ * In memory slf4j appender
+ * Convenient appender to be able to check slf4j invocations
+ */
+public class MemoryAppender extends ListAppender {
+ public void reset() {
+ this.list.clear();
+ }
+
+ public boolean contains(String string, Level level) {
+ return this.list.stream()
+ .anyMatch(event -> event.getMessage().toString().contains(string)
+ && event.getLevel().equals(level));
+ }
+
+ public int countEventsForLogger(String loggerName) {
+ return (int) this.list.stream()
+ .filter(event -> event.getLoggerName().contains(loggerName)).count();
+ }
+
+ public List search(String string) {
+ return this.list.stream()
+ .filter(event -> event.getMessage().toString().contains(string))
+ .collect(Collectors.toList());
+ }
+
+ public List search(String string, Level level) {
+ return this.list.stream()
+ .filter(event -> event.getMessage().toString().contains(string)
+ && event.getLevel().equals(level))
+ .collect(Collectors.toList());
+ }
+
+ public int getSize() {
+ return this.list.size();
+ }
+
+ public List getLoggedEvents() {
+ return Collections.unmodifiableList(this.list);
+ }
+}
diff --git a/testing-modules/testing-assertions/src/test/resources/logback.xml b/testing-modules/testing-assertions/src/test/resources/logback.xml
new file mode 100644
index 0000000000..980ce897ff
--- /dev/null
+++ b/testing-modules/testing-assertions/src/test/resources/logback.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+ %d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/vavr-2/README.md b/vavr-2/README.md
new file mode 100644
index 0000000000..71814a08fd
--- /dev/null
+++ b/vavr-2/README.md
@@ -0,0 +1,8 @@
+## Vavr
+
+This module contains articles about Vavr.
+
+### Relevant Articles:
+- [Introduction to Vavr’s Either](https://www.baeldung.com/vavr-either)
+- [Interoperability Between Java and Vavr](https://www.baeldung.com/java-vavr)
+- [[<-- prev]](/vavr)
diff --git a/vavr-2/pom.xml b/vavr-2/pom.xml
new file mode 100644
index 0000000000..d20dd9afef
--- /dev/null
+++ b/vavr-2/pom.xml
@@ -0,0 +1,27 @@
+
+
+
+ parent-modules
+ com.baeldung
+ 1.0.0-SNAPSHOT
+
+ 4.0.0
+
+ vavr-2
+ vavr-2
+ jar
+
+
+
+ io.vavr
+ vavr-test
+ ${vavr.version}
+
+
+
+
+ 0.9.1
+
+
\ No newline at end of file
diff --git a/vavr/src/main/java/com/baeldung/vavr/either/EitherDemo.java b/vavr-2/src/main/java/com/baeldung/vavr/either/EitherDemo.java
similarity index 100%
rename from vavr/src/main/java/com/baeldung/vavr/either/EitherDemo.java
rename to vavr-2/src/main/java/com/baeldung/vavr/either/EitherDemo.java
diff --git a/vavr/src/test/java/com/baeldung/vavr/either/EitherUnitTest.java b/vavr-2/src/test/java/com/baeldung/vavr/either/EitherUnitTest.java
similarity index 100%
rename from vavr/src/test/java/com/baeldung/vavr/either/EitherUnitTest.java
rename to vavr-2/src/test/java/com/baeldung/vavr/either/EitherUnitTest.java
diff --git a/vavr/src/test/java/com/baeldung/vavr/interoperability/CollectionsInteroperabilityUnitTest.java b/vavr-2/src/test/java/com/baeldung/vavr/interoperability/CollectionsInteroperabilityUnitTest.java
similarity index 100%
rename from vavr/src/test/java/com/baeldung/vavr/interoperability/CollectionsInteroperabilityUnitTest.java
rename to vavr-2/src/test/java/com/baeldung/vavr/interoperability/CollectionsInteroperabilityUnitTest.java
diff --git a/vavr/README.md b/vavr/README.md
index 2b8bb25356..e04e02069f 100644
--- a/vavr/README.md
+++ b/vavr/README.md
@@ -13,5 +13,4 @@ This module contains articles about Vavr.
- [Guide to Collections API in Vavr](https://www.baeldung.com/vavr-collections)
- [Collection Factory Methods for Vavr](https://www.baeldung.com/vavr-collection-factory-methods)
- [Introduction to Future in Vavr](https://www.baeldung.com/vavr-future)
-- [Introduction to Vavr’s Either](https://www.baeldung.com/vavr-either)
-- [Interoperability Between Java and Vavr](https://www.baeldung.com/java-vavr)
+- [[next -->]](/vavr-2)