import de.huepattl.playground.validation.CdiValidatingService;
import de.huepattl.playground.validation.Employee;
import static org.junit.Assert.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.jboss.arquillian.junit.Arquillian;
import javax.ejb.EJBException;
import javax.inject.Inject;
import javax.validation.ConstraintViolationException;
import java.util.Collection;
import java.util.HashSet;
/**
* <p>
* This tests implicit bean and method validation as supported by CDI/EJB. Given
* that we have a CDI managed service
* ({@link de.huepattl.playground.validation.CdiValidatingService}) - whose task
* is not to validate explicitly (it is not a validation service!).</p>
* <p>
* So, we have a managed service and we pass our annotated bean to a service
* method. Just by annotatting the method signature with
* {@link javax.validation.Valid} causes the runtime to validated the given bean
* behind the scenes.</p>
* We also demonstrate passing not NULL and expecting non-NULL results from
* calling a method.
*
* @author Timo Boewing (bjblazko@gmail.com)
* @since 2014-02-12
*/
@RunWith(Arquillian.class)
public class CdiValidationTest extends AbstractValidationTest {
private static final Logger LOG = LogManager.getLogger(CdiValidationTest.class);
/**
* Our CDI managed 'business' service. By definition, this is not an
* explicit validation service.
*/
@Inject
CdiValidatingService service;
@Deployment
public static JavaArchive createDeployment() {
JavaArchive jar = ShrinkWrap.create(JavaArchive.class).addClass(Employee.class).addClass(CdiValidatingService.class)
.addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
return jar;
}
/**
* We have a bean that is valid according to its own constraint definitions
* (see its annotations). When passing the bean to the method, we expect no
* errors. The method argument for the bean is annotated with {@link
* javax.validation.Valid}, which causes the runtime to implicitly validate
* the bean through AOP.
*
* @throws Exception BAM!
*/
@Test
public void validBean() throws Exception {
Employee bean = super.newValidBean();
service.doSomething(bean); // if fails, the unit test throws exception
}
/**
* Let us check if the method's {@link javax.validation.Valid} annotation
* really works. The bean passed is not valid according to its contraint
* annotations, but the service method requires a valid bean - defined by
* the {@link
* javax.validation.Valid} annotation. FIXME: Cannot get JUnit 'expected'
* exception working, receiving {@code ArquillianProxyException} instead.
*
* @throws Exception BAM!
*/
@Test
public void invalidBean() throws Exception {
Employee bean = new Employee();
// This is for @BeanConstraints
bean.setLastName("A");
bean.setFirstName("B");
int errors = 0;
try {
service.doSomething(bean);
} catch (EJBException ejbe) {
// TODO: Remove this block!?!
ConstraintViolationException ve = (ConstraintViolationException) ejbe.getCause();
errors = ve.getConstraintViolations().size();
LOG.info("Found validation errors as expected:");
super.printValidationMessages(ve.getConstraintViolations());
} catch (ConstraintViolationException cve) {
errors = cve.getConstraintViolations().size();
LOG.info("Found validation errors as expected:");
super.printValidationMessages(cve.getConstraintViolations());
}
assertTrue(errors > 0);
}
/**
* We pass NULL to the method. Since it is annotated with
* {@link javax.validation.constraints.NotNull}, it may not continue.
*
* @throws Exception BAM!
*/
@Test//(expected = ConstraintViolationException.class)
public void notNullArgument() throws Exception {
boolean gotException = false;
try {
service.doSomething(null);
} catch (EJBException wrapped) {
ConstraintViolationException ve = (ConstraintViolationException) wrapped.getCausedByException();
gotException = true;
}
assertTrue(gotException);
}
/**
* Test if it also works when passing a bunch of invalid beans in a
* collection.
*
* @throws Exception BAM!
*/
@Test
public void testManyInvalidBeans() throws Exception {
int errors = 0;
Collection<Employee> list = new HashSet<>();
for (int i = 0; i < 10; i++) {
list.add(new Employee());
}
try {
service.doSomethingManyBeans(list);
} catch (EJBException wrapped) {
ConstraintViolationException ve = (ConstraintViolationException) wrapped.getCausedByException();
errors = ve.getConstraintViolations().size();
LOG.info("Found validation errors as expected:");
super.printValidationMessages(ve.getConstraintViolations());
}
assertTrue(errors > 0);
}
/**
* This test just checks that if there is only ONE invalid bean the method
* not executes like desired.
*
* @throws Exception BAM!
* @see #testManyInvalidBeans()
*/
@Test
public void testThatEvenOneInvalidBeanFails() throws Exception {
int errors = 0;
Collection<Employee> list = new HashSet<>();
// Add 10 VALID beans.
for (int i = 0; i < 10; i++) {
list.add(super.newValidBean());
}
// Add one INVALID bean.
list.add(new Employee());
try {
service.doSomethingManyBeans(list);
} catch (EJBException wrapped) {
ConstraintViolationException ve = (ConstraintViolationException) wrapped.getCausedByException();
errors = ve.getConstraintViolations().size();
LOG.info("Found validation errors as expected:");
super.printValidationMessages(ve.getConstraintViolations());
}
assertTrue(errors > 0);
}
/**
* Test validation groups. When manually calling a validator via {@link javax.validation.Validator#validate(Object,
* Class[])}, you can specify the group scope. However, with
* {@link javax.validation.Valid}, this is not the case and we have to remap
* the matching groups using the
* {@link javax.validation.groups.ConvertGroup} annotation.
*
* @throws Exception BAM!
*/
@Test
public void testGroups() throws Exception {
Employee bean = super.newValidBean();
service.doSomethingWithGroups(bean);
int errors = 0;
bean.setStricterValueThanOthers("AC");
try {
service.doSomethingWithGroups(bean);
} catch (EJBException wrapped) {
ConstraintViolationException ve = (ConstraintViolationException) wrapped.getCausedByException();
errors = ve.getConstraintViolations().size();
LOG.info("Found validation errors as expected:");
super.printValidationMessages(ve.getConstraintViolations());
}
assertTrue(errors > 0);
}
/**
* We will call a method that will return {@code NULL} although it has a {@link
* javax.validation.constraints.NotNull} annotation. Thus, we will get a
* validation exception when calling the method.
*
* @throws Exception BAM!
*/
@Test
public void testReturningNull() throws Exception {
boolean foundThatNullWasReturned = false;
try {
service.methodReturningNullByAccident();
} catch (EJBException ex) {
foundThatNullWasReturned = true;
}
assertTrue(foundThatNullWasReturned);
}
/**
* This tests that in a method (two) method arguments are the same. Of
* course, you might check anything between the arguments. The use case
* demonstrated however is common for UI related user input of passwords or
* email addresses (two inputs tht have to match)
* .<p>
* We use a custom validation annotation
* ({@link de.huepattl.playground.validation.annotation.EqualParams}) for
* this, backed by a custom validator
* ({@link de.huepattl.playground.validation.validator.EqualParamsValidator})
* that uses
* {@link javax.validation.constraintvalidation.SupportedValidationTarget}
* with {@link
* javax.validation.constraintvalidation.ValidationTarget}.
*
* @throws Exception BAM!
*/
@Test
public void testEqualMethodParams() throws Exception {
int errors = 0;
service.specifyEmail("same@example.org", "same@example.org");
try {
service.specifyEmail("same@example.org", "different@example.org");
} catch (EJBException wrapped) {
ConstraintViolationException ve = (ConstraintViolationException) wrapped.getCausedByException();
errors = ve.getConstraintViolations().size();
LOG.info("Found validation errors as expected:");
super.printValidationMessages(ve.getConstraintViolations());
}
assertTrue(errors > 0);
}
}