Testing method validation on RestEasy endpoints using Arquillian
tdziurko Mar 28, 2013 10:55 AMIn our project we have Restful services deployed on JBoss 7. To get rid of some boilerplate code we've decided to add some method validations using JSR-303 annotations. Basic setup is working, standard validations are testable with some work, but at least they are working. Problem is with some more sophisticated custom made validators that needs something to be injected. For example: let's say I have annotation and its validator like below. On real server it works like a breeze, but I have problem with preparing setup using Arquillian to test it properly.
I started adding more and more dependencies to @Deployment and so far I can not find the end of this Last two steps were to add in arquillian.xml:
<property name="javaVmArguments">-Djava.util.logging.manager=org.jboss.logmanager.LogManager</property>
as I was getting error on missing java.util.logging.manager propery and then UriBuilder.clas to @Depoyment. But after adding it thing went completely insane and my last stacktrace looks like follows:
15:34:43,047 WARN [org.jboss.modules] (http--127.0.0.1-8080-1) Failed to define class org.jboss.security.SecurityRolesAssociation in Module "org.picketbox:main"
from local module loader @c9be79a (roots: /Users/tomek/work/softwaremill/jboss-as-7.1.1.Final/modules): java.lang.OutOfMemoryError: PermGen space
Question is: Am i doing everything correctly? I ended with adding more and more strange classes to the test dependency and still wasn't able to have this setup working
Source code I am using:
validation annotation:
@Target( { PARAMETER, FIELD })
@Retention(RUNTIME)
@Constraint(validatedBy = CustomerNotExistsConstraintValidator.class)
@Documented
public @interface CustomerNotExistsConstraint {
String message() default "Customer with given name exists";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
and its validating class which needs CustomerService to be injected:
public class CustomerNotExistsConstraintValidator implements ConstraintValidator<CustomerNotExistsConstraint, String> {
@Inject
private CustomerService customerService;
@Override
public void initialize(CustomerNotExistsConstraint constraintAnnotation) {
}
@Override
public boolean isValid(String customerName, ConstraintValidatorContext context) {
boolean customerExists = customerService.customerExists(customerName);
return !customerExists;
}
}
My files:
arquillian.xml content:
<defaultProtocol type="Servlet 3.0" />
<container qualifier="jboss" default="true">
<configuration>
<property name="jbossHome">/Users/tomek/work/jboss-as-7.1.1.Final/</property>
<property name="javaVmArguments">-Djava.util.logging.manager=org.jboss.logmanager.LogManager</property>
</configuration>
</container>
test-validation.xml content:
<constraint-validator-factory>
org.jboss.seam.validation.InjectingConstraintValidatorFactory
</constraint-validator-factory>
and test class itself:
package com.softwaremill.test;
import com.google.common.base.CharMatcher;
import com.softwaremill.beans.CustomerService;
import com.softwaremill.constraints.CustomerNotExistsConstraint;
import com.softwaremill.constraints.CustomerNotExistsConstraintValidator;
import com.softwaremill.constraints.PhoneNumberConstraint;
import com.softwaremill.constraints.PhoneNumberConstraintValidator;
import com.softwaremill.model.Member;
import com.softwaremill.rest.JaxRsActivator;
import com.softwaremill.rest.MemberResource;
import com.softwaremill.rest.MemberResourceRESTService;
import com.softwaremill.rest.RestRequestStatus;
import com.softwaremill.rest.RestResponse;
import com.softwaremill.rest.ValidationExceptionMapper;
import junit.framework.Assert;
import org.apache.commons.lang3.StringUtils;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.container.test.api.RunAsClient;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.jboss.logmanager.ExtHandler;
import org.jboss.logmanager.Level;
import org.jboss.logmanager.LogManager;
import org.jboss.logmanager.log4j.handlers.Log4jAppenderHandler;
import org.jboss.resteasy.client.ClientRequest;
import org.jboss.resteasy.client.ClientResponse;
import org.jboss.resteasy.client.ProxyFactory;
import org.jboss.resteasy.plugins.providers.RegisterBuiltin;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.jboss.resteasy.spi.validation.ValidateRequest;
import org.jboss.seam.validation.InjectingConstraintValidatorFactory;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.jboss.solder.beanManager.BeanManagerAware;
import org.jboss.solder.beanManager.BeanManagerLocator;
import org.jboss.solder.beanManager.BeanManagerProvider;
import org.jboss.solder.beanManager.BeanManagerUnavailableException;
import org.jboss.solder.logging.Logger;
import org.jboss.solder.logging.internal.BasicLogger;
import org.jboss.solder.servlet.beanManager.ServletContextAttributeProvider;
import org.jboss.solder.util.Sortable;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.jboss.resteasy.plugins.validation.hibernate.HibernateValidatorContextResolver;
import javax.inject.Inject;
import javax.validation.Validator;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.MediaType;
import java.net.URL;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@RunWith(Arquillian.class)
@RunAsClient
public class MemberResourceRestTest {
private static final String RESOURCE_PREFIX = JaxRsActivator.class.getAnnotation(ApplicationPath.class).value().substring(1);
@Deployment
public static WebArchive createDeployment() {
return ShrinkWrap.create(WebArchive.class, "test.war")
.addClass(Member.class)
.addClass(MemberResourceRESTService.class)
.addClass(MemberResource.class)
.addClass(RestResponse.class)
.addClass(RestRequestStatus.class)
.addClass(CustomerService.class)
.addClass(JaxRsActivator.class)
.addClass(ValidateRequest.class)
.addClass(CustomerNotExistsConstraint.class)
.addClass(CustomerNotExistsConstraintValidator.class)
.addClass(PhoneNumberConstraint.class)
.addClass(PhoneNumberConstraintValidator.class)
.addPackage(HibernateValidatorContextResolver.class.getPackage())
.addClass(ValidationExceptionMapper.class)
.addPackage(CharMatcher.class.getPackage())
.addClass(StringUtils.class)
// here I started adding stuff needed to use my custom made constraint validator but required stuff is getting stranger and stranger
.addClass(InjectingConstraintValidatorFactory.class)
.addClass(BeanManagerAware.class)
.addClass(BeanManagerLocator.class)
.addClass(BeanManagerUnavailableException.class)
.addClass(BeanManagerProvider.class)
.addClass(Sortable.class)
.addClass(org.jboss.solder.util.service.ServiceLoader.class)
.addClass(Logger.class)
.addPackage(org.jboss.solder.logging.internal.Logger.class.getPackage())
.addClass(LogManager.class)
.addPackage(Level.class.getPackage())
.addClass(ExtHandler.class)
.addClass(org.jboss.logmanager.handlers.FlushableCloseable.class)
.addClass(UriBuilder.class)
// standard stuff here
.addAsResource("META-INF/test-persistence.xml", "META-INF/persistence.xml")
.addAsResource("META-INF/test-validation.xml", "META-INF/validation.xml")
.addAsResource("import.sql")
.addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml")
.addAsWebInfResource("test-ds.xml", "test-ds.xml");
}
@ArquillianResource
URL deploymentUrl;
@BeforeClass
public static void initResteasyClient() {
RegisterBuiltin.register(ResteasyProviderFactory.getInstance());
}
@Test
public void should() {
MemberResource client = ProxyFactory.create(MemberResource.class, deploymentUrl.toString() + RESOURCE_PREFIX);
RestResponse response = client.createCustomerWithUser("contactName", "valid@email.com",
"user", "password", "customer", "reseller", "489999", "true");
assertNotNull(response);
// Then
assertEquals(RestRequestStatus.OK.getStatusCode(), response.getStatus().intValue());
}
}