I'm trying to wrap my head around Arquillian and perhaps even start using it in my project. I have a simple Java web app that deploys as a WAR to Tomcat.
In my project, I define a ServletContextListener impl so that I can execute code when Tomcat starts and stops the application.
I'm trying to write a super-simple Arquillian test class that uses ShrinkWrap and:
Confirms that my bundled WAR can be deployed to Tomcat and started without throwing exceptions; and
Can access a simple system property once the app is running (that the ServletContextListener checks for); and
Confirms that when Tomcat shuts down, no exceptions are thrown (clean shutdown)
Also, my class that implements ServletContextListener is called AppLifecycleManager:
public class AppLifeCycleManager implements ServletContextListener {
private String logLevel;
// Injected by Guice, but that's not really relevant for this question.
#Inject
private Logger logger;
// Getter and setter for logLevel and logger
#Override
public void contextInitialized(ServletContextEvent event) {
logLevel = System.getProperty("log.level");
}
#Override
public void contextDestroyed(ServletContextEvent event) {
logger.info("Peacefully shutting down the application.");
}
}
So far, here's my best attempt:
#RunWith(Arquillian.class)
public class MyFirstRealIntegrationTest {
#Deployment
public static Archive<?> createDeployment() {
// Haven't figured this part out yet, but for the sake of
// this question lets pretend this returns a properly-packaged
// WAR of my web app, the same that my Ant build currently produces.
}
#Test
public void shouldBeAbleToStartTomcatWithoutExceptions() {
// Given
Archive war = createDeployment();
// When - deploy war to Tomcat container
try {
// ??? how to access/init a Tomcat container?
TomcatContainer tomcat = new TomcatContainer(); // this is wrong
tomcat.start();
} catch(Throwable throwable) {
// Starting the container should not throw exceptions
Assert.fail();
}
}
#Test
public void shouldBeAbleToStopTomcatWithoutExceptions {
// Same setup as above test but stops tomcat and checks for
// thrown exceptions. Omitted for brevity.
}
#Test
public void shouldHaveAccessToSysPropsOnceRunning() {
// Here, deploy to the container and start it.
// Then, confirm that AppLifecycleManager correctly read
// the log.level system property.
// Given
Archive war = createDeployment();
TomcatContainer tomcat = new TomcatContainer();
// When - AppLifeycleManager should now read the system property
tomcat.start();
// Then - make sure log.level was set to "DEBUG" and that it was
// correctly read by AppLifeCycleManager.
Assert.assertTrue(war.getClass(AppLifeCycleManager.class)
.getLogLevel().equals("DEBUG"));
}
}
So, given my approach here, I immediately have several problems:
I'm not sure how to access/instantiate my Tomcat container so that it can even be started/stopped
I'm not sure how to actually execute tests from inside my running/deployed web app. In the 3rd test above I used war.getClass(AppLifeCycleManager.class).getLogLevel() to try and get access to a "live" class instance and check its logLevel property's runtime value, but I know this is wrong.
So I ask: how would a battle-worn Arquillian veteran write these 3 simple tests, and how do I actually go about performing tests on my "running" web app from inside the JUnit test? Thanks in advance.
I dont't think you should handle the startup/shutdown of tomcat in your test. I would by much easier if you used the embedded tomcat container: https://docs.jboss.org/author/display/ARQ/Tomcat+7.0+-+Embedded. In this case, arquillian will handle the startup and shutdown of tomcat.
You should create the deployment in the method annotated with #Deployment. Just read the following guide: http://arquillian.org/guides/getting_started/
You're almost there. createDeployment manages the lifecycle of your embedded container for you (starts and stops the virtual container automatically). That way, you're just focussing on the tests themselves. To write your integration tests, there's no "framework" or "Arquillian API" to code against. You just call classes and methods the way your main code does.
The key thing here is: your tests are actually running inside the container. If an exception occurs, or if your assert fails, the Arquillian runner throws an exception and stops the container. For your code example, where you're testing to see if you can read a system property:
#RunsWith(Arquillian.class)
public class MyArquillianTest {
#Deployment
public Archive<?> createDeployment() { ... }
#Test
public void shouldBeAbleToReadSysPropAtStartup() {
Assert.assertTrue(System.getProperty("log.level") != null);
}
}
Remember, Assert.assertTrue(System.getProperty("log.level") != null) Arquillian is "transporting" your code inside the container you configure it for. So that assert is actually running inside your deployed container.
Related
I am creating a service which watch for a file and as soon as file is available it loads it to db. Once this job is done I want to shutdown the app gracefully.
But when I use context.close it throws exception (though the app shuts down) but I want to shut it down without causing any exception.
#profile("non-test")
class Manager implements ApplicationContextAware{
#PostConstruct
public void init()
{
//watch for file and as soon as file is available invokde trigger()
}
public void trigger()
{
//load to db
shutodown()
}
public void shutdown()
{
SpringApplication.exit(context);
}
}
when i call shutdown it throws below exception.
AnnotationConfigApplicationContext has already been closed
I want the app to shutdown gracefully without any exception.
You problem is most probably presence of Spring dependency that makes your app long lived. Such dependency is for example spring-boot-starter-web. This starter includes Servlet container as dependency ans Spring will by default be running forever.
If non of such dependencies is on classpath, your Spring Boot app would shut down automatically (without any special effort to kill it). I would suggest to take a look at some of Spring guides for batch job.
I have a moderately heavy springboot service, it takes 10-15 seconds to boot on a happy flow, and (1-2) minutes to fail on a retry/failover flow. This is ok for my business flows, and is how I expect a healthy service to behave.
I have integration tests (that run some end-to-end flows in my service), that can only test the actual integration status while the test machine (or dev machine) is connected to a specific VPN.
I want to auto skip integration tests if I'm not connected to VPN.
consider the following code
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {Server.class}, // auto scans a bunch of components
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) // slow loading context
public class IntegrationTest {
#BeforeClass
public static void beforeClass() {
Assume.assumeTrue(DnsTool.vpnConnected()); // fast failing test
}
#Test
public void testIntegration() {
// some test logic
}
}
When the assumptions pass, my tests run, and all is good.
When the assumptions fail, my tests get skipped, but only after trying to load my expensive context.
How can I avoid the long running time for my test suite?
Things I tried:
Subclassing SpringJUnit4ClassRunner, and overriding isTestMethodIgnored.
Adding a TestExecutionListener, and throwing the assumption exception in beforeTestClass
These made no impression on Spring, and the context got loaded any way.
Things I didn't try:
Lazy init comes with 2.2.X next stable release of spring I think.
Lazy init potentially makes my problem go away, but I feel like there should be some easy spring-test/junit fix that I'm missing.
Thanks in advance for the help.
To me, this sounds like something that you shouldn't do in tests at all.
Tests (at least IMHO), are supposed to check the business cases and assume that the environment is set up and ready.
Maybe it worth to delegate this functionality to build tool and CI.
Example:
Define a profile in maven (or whatever build tool you use) that will run integration tests that require VPN. Define profile that will run all the rest of integration tests as well.
Activate the profile if some system property is available.
In CI tool (like Jenkins) as a part of CI even before you run maven, run the script that will check the VPN connection. Based on the results set the system properties and run maven with these properties. The required profiles will be loaded and all the tests / only tests that do not require VPN will be run.
Update
If you need to make it work from Spring (and it looks like you prefer this way),
Spring has a special annotation called #IfProfileValue
By default, it matches against system properties and if the value doesn't match the test gets ignored.
It looks something like this (and note that you can put this annotation on class as well, then it will work for all test methods in the class):
#RunWith(SpringRunner.class)
#SpringBootTest
public class MyTestClass {
#IfProfileValue(name = "os.name", values = {"Linux"})
#Test
public void testMe() {
// will run only in linux, otherwise
// won't even try to load an
// application context
....
}
}
This covers the case when you resolve the VPN connectivity externally and run the tests with a property. However, if you want to implement the VPN connectivity check in java, this annotation along not enough because it can work only with Java system properties, so in order to work with custom logic you need to implement org.springframework.test.annotation.ProfileValueSource:
public class VPNConnectivityProfileValueSource implements ProfileValueSource {
private String vpnEnabled = "true";
public VPNConnectivityProfileValueSource () {
// no spring context is available here
ClassPathResource resource = new ClassPathResource("vpn-config.properties");
if (resource.exists()) {
// read the VPN address,
//
//this.testProps = PropertiesLoaderUtils.loadProperties(resource);
// invoke your utility, check the connectivity, etc.
this.vpnEnabled = ...
}
}
#Override
public String get(String key) {
// this is important method,
if(key.equals("vpn.enabled") {
return this.vpnEnabled;
}
else return System.getProperty(key);
}
}
The last thing is to make the test aware of the ProfileValueSource:
For this there is another special annotation that you put on the test:
#ProfileValueSourceConfiguration(VPNConnectivityProfileValueSource.class)
All in all it the test can look like this:
#RunWith(SpringRunner.class)
#SpringBootTest
#ProfileValueSourceConfiguration(VPNConnectivityProfileValueSource.class)
#IfProfileValue(name = "vpn.enabled", value = "true")
public class MyTestClass {
#Test
public void testMe() {
....
}
}
All the classes/annotations I've mentioned reside in package org.springframework.test.annotation
I have a number of very complex integration tests written with use of Spring Test framework. What I would like to do is to fetch last n lines from server log file in case test is failed.
For this purposes I created test:
#TestExecutionListeners({LogFetchingTestListener.class})
public MyComplexTestClass extends AbstractTestNGSpringContextTests {
#Test
public void myComplexTest() {
// Here goes the test logic...
}
}
and test listener:
public class LogFetchingTestListener extends ABstractTestExecutionListener {
#Override
public void afterTestExecution(TestContext context) {
// Use some spring beans to get entities to connect to servers
}
}
Everything is fine but I was unable to find properties in TestContext or somewhere else which will let me know if my test has failed or not.
It is pretty critical as I have a big number of tests and it is completely senseless to fetch logs for test cases that were run successfully.
Do Spring provide this information in test listeners? If no, is there any workarounds for this?
Thank you.
I'd recommend overriding afterTestMethod(TestContext) instead of afterTestExecution(TestContext), since the former will be executed after #AfterMethod lifecycle methods in the test class.
To obtain the exception (i.e., the cause of a failed test), you just need to access testContext.getTestException().
I have this scenario:
Enterprise Application. Runs ok. --> contains MyFacade
Client application. Runs ok.
Ejb module. Runs ok, but can't test --> contains MyClass.java
DomainLibrary. --> contains entities and remote interfaces.
I want to run test file of point 3. (ejb module). Ok, first of all I put the Enterprise Application running. On second place I run the test file of the point 3.
The problem is that the remote interface ejb contained on Enterprise Application can't be found.
Error 1: It end up with an endless loop of the following output
WARNING: AS-CDI-005 Okt 22, 2013 4:49:23 PM org.glassfish.weld.BeanDeploymentArchiveImpl handleEntry
Error 1: Solved Running JUnit Tests on embedded glassfish 4 causing WARNING: AS-CDI-005
Error 2!!!:
javax.naming.NamingException: Lookup failed for [...] (MyFacade)
Ejb module: MyClass.java
#Singleton
public class MyClass implements MyClassLocal {
#EJB(lookup = "java:global/EnterpriseApplication-ejb/MyFacade!com.mydomain.repository.MyFacadeRemote")
private MyFacadeRemote myFacade;
public MyClass() {
}
public void bussinesMethod(){
System.out.println("Hello stackOverWorld! ");
myFacade.findAll();
}
}
Test method:
#Test
public void testBusinessMethod() throws Exception {
System.out.println("businessMethod");
Map<Object, Object> properties = new HashMap<Object, Object>();
properties.put(EJBContainer.APP_NAME, "MyEjbModule");
properties.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.enterprise.naming.SerialInitContextFactory");
EJBContainer container = EJBContainer.createEJBContainer(properties);
MyClassLocal instance = (MyClassLocal)container.getContext().lookup("java:global/MyEjbModule/classes/MyClass!com.mydomain.MyClassLocal");
//EJBContainer container = javax.ejb.embeddable.EJBContainer.createEJBContainer();
//MyClassLocal instance = (MyClassLocal)container.getContext().lookup("java:global/classes/MyClass");
instance.businessMethod();
container.close();
}
You may use the same approach we did for our EJB application. You may call some of the tests 'unit tests' but they are really integration tests. Running these tests includes starting the application server first, then running the JUnit test cases and finally stopping the server. These test cases act as the client, loading the ejb remote interface, make the appropriate calls, validate the return values, etc just like your client would.
One of the problems with this is that these tests can take a while to run, depending on how many you have. It also requires the server be up and running.
I know there must be better ways, but this has worked for our project for about 10 years.
This is a follow up question to my previous question.
I am trying to write test case for my ServiceLocator class but it gives me the following error:
com/iplanet/ias/admin/common/ASException
java.lang.NoClassDefFoundError: com/iplanet/ias/admin/common/ASException
at java.lang.ClassLoader.defineClass1(Native Method)
My test case:
public void testServiceLocator () throws UivException, NamingException
{
DataSource ds = ServiceLocator.getInstance().getDataSource("jdbc/RSRC/my/mydb");
//I have not put any assert statements because i know above line is failing
}
The above code fails on getInstance() method which looks like this:
static public ServiceLocator getInstance() throws UivException {
try {
if (me == null) {
synchronized(ServiceLocator.class) {
me = new ServiceLocator();
}
}
return me;
}
catch (UivException e) {
throw new UivException(ErrorCode.SERVICE_LOCATOR_ERROR,
ErrorCode.SERVICE_LOCATOR_LOOKUP_ERROR,
e.getMessage());
}
}
I know this ServiceLocator works fine because when I test my application from the front end there are no issues. Only reason I am writing this test case is because I want to test my DAO. And for me to test my DAO ServiceLocator has to work (from JUnit).
I don't have a clue what to make of the error message. Does anyone want to suggest something I can try that will hopefully work?
EDIT: ServiceLocator constructor
private ServiceLocator() throws UivException {
try {
ic = new InitialContext();
// System.out.println("Created the Initial Context");
cache = Collections.synchronizedMap(new HashMap());
}
catch (NamingException ne) {
throw new UivException(ErrorCode.SERVICE_LOCATOR_ERROR,
0, ne.getMessage());
}
catch (NullPointerException e) {
throw new UivException(ErrorCode.SERVICE_LOCATOR_ERROR,
0, e.getMessage());
}
}
Actually, the error is pretty clear: java.lang.NoClassDefFoundError: com/iplanet/ias/admin/common/ASException indicates that the definition of ASException cannot be found at runtime.
When running in the application server, this class is provided by iPlanet Application Server and the code just runs fine. To run this code outside the application server context, you'll have to provide it "manually" by putting it to the classpath. So you have to add the right iPlanet JAR to the classpath (this is the tricky part, you'll have to find which one).
Additionally, I can see taht you're using the InitialContext non-arg constructor so you're using iPlanet's environment settings when running inside the application server. Outside iPlanet (e.g. for a unit test), you'll have to provide the right JNDI properties for iPlanet yourself. To do so, you'll have to put a jndi.properties file on the classpath with (at least) the initial context factory and the provider URL. Something like this:
java.naming.factory.initial=...
java.naming.provider.url=...
Check your iPlanet documentation for the values.
Well, is com.iplanet.ias.admin.common.ASException on the classpath when you invoke your tests?
Are you relying on the app server to have the JDBC driver's libraries in it's classpath, or are you deploying it yourself?
I'm curious why you're writing a service locator. Is it part of an assignment? In the real world, Dependency Injection is the clearly superior choice, using either Guice or Spring.