How to mock/stub Java container in unit testing? - java

I have an object that implements ServletContextListener which, according to the Java EE servlet spec, is invoked by the servlet container at deployment/startup time via its contextInitialized(ServletContext) method.
I am trying to write a unit test that simulates a servlet container starting up and deploying my ServletContextListener (so that I can determine if the app is initializing correctly).
Is this possible, and if so, how? Is it container-specific? If so I am using OGS. Looking for code that looks something like this:
#Test
public void shouldBootstrapAppAtDeployTime() {
// Given
GlassFishContainer container = new GlassFishContainer(); // ha!
MyApp app = mock(MyApp.class); // MyApp implements ServletContextListener
// When - deploy app to container
// Causes app.contextInitialized(ServletContext) to be called
container.deploy(app);
// Then - verify the method was called with any ServletContext
mock.verify(app.contextInitialized(Matchers.any());
}
This is sloppy pseudo-code with some poorly-implemented Mockito sprinkled in for demo purposes (just to get my intentions across). Thanks in advance!

Maybe you can solve this with Arquillian: http://arquillian.org/features/
From the web page:
Arquillian brings your test to the runtime, giving you access to container resources, meaningful feedback and insight about how the code really works.

I think your approach is slightly mistaken. What your unit test pseudocode does is testing the container logic that it correctly calls your ServletContextListener - I would trust the container doing that properly.
What I would do is testing your listener class directly, instantiate it, call the contextInitialized() method with a mock ServletContext (I see you're familiar with Mockito, an excellent tool for the job!), and do your assertions if your ServletContextListener did its job as expected.

Not sure if there is such testing framework. Even if it exists, it will not be testing in the real environment(which might be different from the testing environment).
The best way to write this kind of testcases is :
Deploy the war using ANT, the server you are using might be providing an MBean or something to deploy the application
Your listener might be doing some functionality when contextInitialized() method was invoked.
Write a client which makes a request to server and checks if the step 2 was successful or not.

Related

Integration tests of Spring application

I am trying to implement integration tests for my Tomcat application, but my issue is that the application is launched separately from the tests so the tests cannot access the application context and neither the database.
My idea is running the tests "within" the running application, so I can #Autowire EntityManager and check for instance the state of the database during testing or even create database entities for testing.
My only idea of doing this is to actually run the application programmatically from the tests as ClassPathXmlApplicationContext("applicationContext.xml") and the access the Context. This would work, but it would be very hard for debugging as we wouldn't be able to use Hotswapping during the testing. Also I guess the server would be stopped as soon as the tests would end. I guess that is not the best and correct solution.
EDIT:
My question was probably unclear, so I will try to clarify.
I have a Tomcat application with Spring and Hibernate. The Spring beans and Hibernate database connection is initialised when the Tomcat application is started. The issue is how to run the tests of the active Spring beans from methods annotated with #Test in src/test/java which are started separately.
Consider this class:
#Component
class MyRepository {
#Autowired
EntityManager em;
#Transactional
public void myMethod(MyEntity entity) {
// do some job with entity
...
em.flush();
}
}
This class will be initialised with Tomcat as a MyRepository bean.
To test it, I cannot just call new MyRepository().myMethod(...) - I need to access the bean. The issue is accessing the bean from the #Test method:
#Test
void testMyRepository() {
Item item = ...
// then use the repository to handle the entity
context.getBean(MyRepository.class).myMethod(item);
// then assert the state of the database
context.getBean(EntityManager.class).find(Item.class, ...) ...
}
I can probably get the context in the initialisation of the tests with
ApplicationContext context = ClassPathXmlApplicationContext("applicationContext.xml");
But it would mean launching the whole application each time the tests are started. The better solution would be if the application could run separately from the tests.
Hope my problem is more clear now.
I would suggest you to use the SpringRunner to start the Spring application context and perform your tests on that running instance. You can customize the context the way it doesn't contain parts you don't want to tests and you can create mocks for components that require some external resources (REST clients and such). Take a look at the Spring docs or Spring Boot docs.
If multiple tests use the same Spring context configuration, the context is started just once and reused. So it's good to have it's configuration in a parent class of your tests. You can autowire any Spring bean into your test and test it.
You can use an in-memory database (such as H2) instead of a production one, so your tests are not dependent on an external infrastructure. To initialize the database, use tools like Flyway or Liquibase. To clear the database before each test, you can use the #Sql annotation.
You can find many examples of projects with such tests, for example my own demo.
If you want to test an external system, I would suggest something like JMeter.
Unfortunately you cant mirror your classes and use them in your tests. Thats a big disadvantage of web services. They always depend on user / machine interaction. With a lot of effort you can extract the functionality of the essential classes or methods and construct test scenarios etc. with jUnit.
The Overview of your possibilities:
special drivers and placeholders
you can use a logger with detailed log-level and file output. Then you created scenarios with the expected result and compare it with your log files.
Capture replay tools. They record your exection and replay them for monitoring.
I can also recommend using Selenium for the frontend tests.
Hope it helped.

Struts2 pre-test ServletContext

I have two Struts2 Action classes - 'SetupAction' and 'MainAction'. 'SetupAction' sets up some file locations to read files. It implements the ServletContextListener:
#WebListener
public class SetupAction implements ServletContextListener {....}
I want to test methods in my 'MainAction' using JUnit4, in addition to using Cargo and Failsafe plugins to start a Tomcat7 instance before running tests and tearing it down afterwards.
The problem is I need 'SetupAction' to complete setup before running the tests. I want to run this as if it's on a real server which is why I'm avoiding mocks.
proxy.getAction() in 'MainAction' will return 'SetupAction' if I send a HTTP GET request to the server at the start of the test.
Any help/guidance would be greatly appreciated.

Is it possible to test an HttpSessionListener with Spring-MVC and Spring-Test-MVC?

We have some logic in an HttpSessionListener that we'd like to test. We're using Spring MVC and the associated testing framework, with MockMvc and so on. Unfortunately, we haven't found a way to get the listener initialized so that when a session is created or destroyed the appropriate listener methods are called.
I have tried to add the listener programmatically rather than using web.xml (as described in this question) and this works fine when running in a Servlet 3.0 container. But when running with Spring Test it all goes wrong, as the MockServletContext does not support the addListener method, and so throws an exception.
Is there any way to test such listeners without using integration testing?
The Servlet container decides when to dispatch events to the HttpSessionListener. This is not necessarily right after a session created or destroyed. Because this depends on the container implementation, you can't depend on the unit test. Integration testing is the way to go.
You can always unit test those HttpSessionListener implementations
HttpSessionListener listener = new MyHttpSessionListener();
listener.sessionCreated(mockEvent);
listener.sessionDestroyed(mockEvent);
outside the context of your application.

Clean up after servlet if init() failed

I have an Initializer class that implements the ServletContextListener interface. In its contextInitialized() method, I initialize some global classes that have to be destroyed, otherwise the servlet cannot be unloaded.
However, when the servlet's init() method throws a ServletException, the contextDestroyed() method never gets called --> resources are not release --> servlet does not get unloaded by Tomcat (it remains in "running" state even though its init method never finished).
My question is this - how do I cleanup the resources in this case?
Bonus: why does the servlet even get to "running" state? I understand from the documentation that it's not supposed to be running unless the init() method finishes successfully.
Edit - I think this is because each status line displayed in Tomcat Manager represents an entire war, and not a servlet. A war may contain several servlets, with some succeeding to start and others not. The Initializer is called when the container starts, and its destroy is called only when the entire container is dropped. This leads to a related question - is there a similar built-in way to monitor the state of individual servlets?
(I'm aware I can write custom code to monitor the servlet, either via JMX, or not, but that's out of the scope of
this question).
As far as I can tell there's absolutely no way to do so without an external request. ServletContextListener gives you the correct signal (when all servlets have been initialized - successfully or not), but you can't enumerate all servlets in the context to test their status because the relevant ServletContext methods were deprecated and now return an empty enumerator.
In short, the only way to do this is via a nonstandard API; in particular, it's almost trivial to do this with Tomcat's JMX API, which is the course I'd recommend.
In real world the init() should never fail. If it fails, then it is a programming error which the developer is supposed to fix. The appserver webcontainer has nothing to do with it. The servlet will simply be kept unavailable.
What container are you running?
Tomcat for example does support JMX. You can always write your own JMX-beans.

How to approach shifting a Java Swing app to a Servlet?

I want to use the Model-View-Controller template while writing my Web App. The problem is, the Model part of the code has already been written in Swing. The Model code also must require the container to call its main method before any interaction with its servlets. So is there a way for me to specify the location of the main method in the Deployment Descriptor so that the container calls the main method and compiles the code, and then, keeps it running for the entire duration the server is running without in any way restarting or recompiling the model class in between.
Try looking into load-on-startup parameter of servlet in Deployment Descriptor (DD). Precisely, it will load that particular servlet on server start-up.
Moreover, you should read about request lifecycle, request/session/application context. And you must look into JSP (or any other popular technology) for creating V of MVC. How URL mapping works.
Main method is basically work as an entry point in our application. Whereas in web application there is no particular entry point. Or if there is you can think of a welcome page. You might also want to look into welcome-file-list parameter of DD.
Cheers.
To run initialization when a web app is loaded, you can either use the servlet's init method or a ServletContextListener. You can call the main method from either of those yourself.

Categories