Single integration test for several Spring apps - java

I have two Spring applications which interact which each other via database and some AMQP:
web-application built on Spring MVC
Spring-Boot application
Each application has its independent context and properties files.
What is the proper way of writing single integration test for these two applications?
More specifically: I can merge this two applications in one maven project in order to have access to both of them.
Is it possible to configure test contexts for both applications in
one Spring test? At the moment I have no idea how to tell spring use different contexts for different applications in one test.
Another purpose of this testing is also to obtain code coverage for these two applications. That is why I can not just start, say, Spring-boot application as separate process. Is it possible at all?

Spring's test module brings up a single application context (take a look at the key abstractions section of the official documentation) per test so no, you cannot have multiple application contexts per test.
What you can have is a merged application context that imports both the Spring Boot and Spring MVC application's context; that way, you can test beans from both applications. However, this is probably not what you want to do and it's something I would recommend against - your tests will become almost worthless since making this approach work could probably entail some hacks and you will not be testing your applications realistically given that they will be deployed separately.
You should write per-application integration tests and measure coverage for each of them. If your application is relatively small, you can have an end-to-end testing module that would leverage Docker containers to create an environment similar to your production and verify that your applications correctly work together.

Related

Run Spring Boot jar from standard Java application

I have a large application with a database, Swing UI etc. Now I want to add a REST API for this application. Spring Boot allows easy generation of a REST API with useful features such as OpenApi documentation and authentication.
However when I run the Spring Boot application from within the large non Spring Boot application the Spring Boot application gets confused by the dependencies of the parent application and fails to run.
So my requirement is this: run a Spring Boot application from withing a non Spring Boot application without dependency interference from the parent application. I am currently running the Spring Boot application by adding the executable jar as a dependency and then calling
org.springframework.boot.loader.JarLauncher.main(new String[0]);
to run the Spring Boot application. I am not set on this way of doing it and any suggestions will be greatly appreciated.
Spring is a huge collection of highly configurable software libraries that can be used to setup (among other things) REST API endpoints and OpenAPI documentation and UI.
Spring Boot is a project to simplify the process of using these libraries by applying an opinionated view of how to run them within a standalone process.
By asking how to run a Spring Boot application within a larger application you are trying to get the benefit of the opinionated setup while violating the assumptions that the setup is based on. I guess in theory it might be possible using some sort of handrolled classloader isolation, but once you've solved the dependency problem you'll probably end up with class version conflicts, issues with configuration locations, etc. In short if it is possible at all the effort of doing so would negate the benefit.
There are two ways of resolving the issue.
Use Spring Boot to build your API as a standalone process. Configure the new process to talk to the same database as the existing application. If neccessary factor out any code common to both the existing application and the API (JPA entities, DAO classes etc) into a shared library. If you go with this option you will have the overhead of having to manage multiple kinds of process in your production environment, which is more complex - but has advantages in terms of decoupling scaling, release cycles, restart times. See the debate on microservices (https://martinfowler.com/articles/microservices.html).
Use the Spring libraries that provide REST and OpenAPI features as part of your existing application, without using Spring Boot. You'll need to have SpringMVC set up in order to use #RestController annotated classes. If your existing application is a web application that's not too bad (https://docs.spring.io/spring-framework/docs/3.2.x/spring-framework-reference/html/mvc.html). If it's not run in a webserver already you'll have to launch the SpringMVC framework in an embedded webserver (https://devcenter.heroku.com/articles/create-a-java-web-application-using-embedded-tomcat). There's a good article on adding OpenAPI to an MVC application here: https://www.baeldung.com/spring-rest-openapi-documentation.
you can simply exclude auto-configure dependencies. Here is an informative link https://www.marcobehler.com/guides/spring-boot
Here is a code snippet of how to exclude when applications get started.
#SpringBootApplication(exclude = {DataSourceAutoConfiguration.class)
public class Application {
public static void main(String[] args) {
SpringApplication.run(PayPalApplication.class, args);
}
}
or the other ways
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
I want to share another Spring Framework dependencies https://www.marcobehler.com/guides/spring-framework

How to reuse classes in a Spring Boot REST application?

I have a Spring Boot REST application with JPA entities and Repository classes (and related services) that works very well. Now I would like to reuse these classes for other purposes, like weekly CRON jobs and similar one-time processes which will be run from the command line.
What would be the best way to do this? The challenge is that the persistence context properties are set in application.properties, and the persistence context isn't initialized unless the Application class is initialized.
I can break out all of these classes into a separate project, and use a different way to define the persistence context there, but this becomes more of a maintenance headache if anything changes with the entities or DAO methods.
What I would really like is to have a way, from the command line, to tell Spring Boot to run another class instead of the main Application (and have the persistence context properly initialized). Any way to do this?
(Note I asked a similar question which got no response: Possible to use Spring Boot repositories from another main class?)
[Edit] is it possible to do this by creating a #component that implements the CommandLineRunner? I just want it to run a simple one-time process and not the full REST application.
There are a number of ways you could do this.
You can have multiple Main classes, and then select which application yuo want to start select main class, however if you don't know how ComponetScan works you will end up loading both applications if you are not careful.
Another way is to use Profiles, you can set the profile when you start your spring app, and then have your web profile that will start Tomcat, and a command line profile that will not .
In the project I'm working on we have choosen to have the data-layer as a completly separate module (same gradle project), which has it's own Spring Context. The data-layer spring context is then used as the parent context for other applications, as a reusable component. It is a somewhat cleaner separations of concerns, were the shared code is clearly marked, instead of having multiple applications inside the same code mudule.

Regression component tests with Cucumber. Is there any boundary to the layers that should be tested?

I found myself last week having to start thinking about how to refactor an old application that only contains unit tests. My first idea was to add some component test scenarios with Cucumber to get familiarised with the business logic and to ensure I don't break anything with my changes. But at that point I had a conversation with one of the architects in the company I work for that made me wonder whether it was worth it and what was actually the code I had to actually test.
This application has many different types of endpoints: rest endpoints to be called from and to call to, Oracle stored procedures and JMS topics and queues. It's deployed in a war file to a Tomcat server and the connection factory to the broker and the datasource to the database are configured in the server and fetched using JNDI.
My first idea was to load the whole application inside an embedded Jetty, pointing to the real web.xml so everything is loaded as it would be loaded from a production environment but then mocking the connection factory and the datasource. By doing that, all the connectivity logic to the infrastructure where the application is deployed would be tested. Thinking about the hexagonal architecture, this seems like too much effort having in mind that those are only ports which logic should only be about transforming received data into application data. Shouldn't this just be unit tested?
My next idea was to just mock the stored procedures and load the Spring XMLs in my test without any web server, which makes it easier to mock classes. For this I would be using libraries like Spring MockMvc for the rest endpoints and Mockrunner for JMS. But again, this approach would still test some adapters and complicate the test as the result of the tests would be XML and JSON payloads. The transformations done in this application are quite heavy where the same message type could contain different versions of a class (each message could contain many complex object that implement several interfaces).
So right now I was thinking that maybe the best approach would be to just create my tests from the entry point to the application, the services called from the adapters, and verify that the services responsible to send messages to the broker or to call other REST endpoints are actually invoked. Then just ensure there are proper unit tests for the endpoints and verify everything works once deployed by just providing some smoke tests that are executed in a real environment. This would test the connectivity logic and the business logic would be tested in isolation, without caring if a new adapter is added or one is removed.
Is this approach correct? Would I be leaving something without testing this way? Or is it still too much and I should just trust the unit tests?
Thanks.
Your application and environment sound quite complicated. I would definitely want integration tests. I'd test the app outside-in as follows:
Write a smoke-test suite that runs against the application in the actual production environment. Cucumber would be a good tool to use. That suite should only do things that are safe in production, and should be as small as possible while giving you confidence that the application is correctly installed and configured and that its integrations with other systems are working.
Write an acceptance test suite that runs against the entire application in a test environment. Cucumber would be a good choice here too.
I would expect the acceptance-test environment to include a Tomcat server with test versions of all services that exist in your production Tomcat and a database with a schema, stored procedure, etc. identical to production (but not production data). Handle external dependencies that you don't own by stubbing and mocking, by using a record/replay library such as Betamax and/or by implementing test versions of them yourself. Acceptance tests should be free to do anything to data, and they shouldn't have to worry about availability of services that you don't own.
Write enough acceptance tests to both describe the app's major use cases and to test all of the important interactions between the parts of the application (both subsystems and classes). That is, use your acceptance tests as integration tests. I find that there is very little conflict between the goals of acceptance and integration tests. Don't write any more acceptance tests than you need for specification and integration coverage, however, as they're relatively slow.
Unit-test each class that does anything interesting whatsoever, leaving out only classes that are fully tested by your acceptance tests. Since you're already integration-testing, your unit tests can be true unit tests which stubb or mock their dependencies. (Although there's nothing wrong with letting a unit-tested class use real dependencies that are simple enough to not cause issues in the unit tests).
Measure code coverage to ensure that the combination of acceptance and unit tests tests all your code.

Automated integration tests on deployed Java EE environment?

Our team is currently introducing automated testing to an existing Java EE web application that is deployed on Weblogic. We've had success with unit testing using JUnit and Mockito which are automatically run when our app is built and deployed by Jenkins.
Integration testing has been more challenging because our application relies on components provided by the Java EE container such as WorkManager. There are several Spring beans which require these components to initialize properly. One way we've been able to get around it is to create custom application context configuration files which create mocks of the components that we don't really need for testing but still require to initialize the bean. This has been somewhat of a maintenance nightmare because each integration test needs it's own config and some can be quite involved.
What we really want is to be able to have the entire application initialized with the normal configuration used in a deployed environment when running our integration tests. Is there a way to have integration tests automatically execute after deployment through Jenkins or another tool?
You may want to check Arquillian, it can run test in your containers. Even remote ones.
I qoute:
No more mocks. No more container lifecycle and deployment hassles.
Just real tests!

Is it possible to access java beans in selenium tests?

We are currently developing a trade project with Richfaces 3.0, Seam 2.2 and JBOSS 6.0. And we are using Selenium for our GUI tests. I just want to know if there is any possibilty to access beans in selenium tests.
No, Selenium and Seam beans are a completely different level of abstraction. Seam beans are running inside a JBoss server while Selenium works on top of a web browser.
If you want to somehow control the application from the inside, you must provide some interface for these beans that is accessible via Selenium test. For instance you might expose some operations as web services or JMX beans and access them from Selenium test suite if written in Java.
Note however that this is not the best practice - Selenium tests should only work on user interface (end-to-end) level. Try to setup your application only via the user interface rather than manually accessing application internals.
UPDATE: If you have some common setup (like users, products, etc.), insert them in your database as part of your common deployment infrastructure. Then you can have a single test for creating/accepting a user, adding product, etc. and then simply reuse the common users already existing in the database.
There's nothing technically stopping you from allowing selenium visibility to your beans and call methods on them directly. However, it won't be a good design practice. Selenium is for testing the behaviour of your applications (through GUI, mostly) and should not be concerned with bean level. Maybe if you give us a use case of why you may need to do so it may make sense?
In the past I have exposed web services and JDBC tests via selenium as a shortcut for QA people to test certain parts of the application, if that's what you are talking about, but it may be best to use it via web services as Tomasz mentioned.

Categories

Resources