DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Micronaut With Relation Database and...Tests
  • Java CI/CD: From Local Build to Jenkins Continuous Integration
  • A Systematic Approach for Java Software Upgrades
  • Integrate Cucumber in Playwright With Java

Trending

  • Mastering Advanced Traffic Management in Multi-Cloud Kubernetes: Scaling With Multiple Istio Ingress Gateways
  • AI's Dilemma: When to Retrain and When to Unlearn?
  • Comprehensive Guide to Property-Based Testing in Go: Principles and Implementation
  • Unlocking Data with Language: Real-World Applications of Text-to-SQL Interfaces
  1. DZone
  2. Coding
  3. Java
  4. Testing Java EE (or Why Integration Tests Are Overrated)

Testing Java EE (or Why Integration Tests Are Overrated)

Testing that goes beyond just unit testing and integration testing. Why we should be doing full system testing.

By 
Sebastian Daschner user avatar
Sebastian Daschner
·
Apr. 27, 16 · Tutorial
Likes (7)
Comment
Save
Tweet
Share
25.7K Views

Join the DZone community and get the full member experience.

Join For Free

In (enterprise) development testing often is treated very much as poor relations. But for more than “Hello World” applications testing is not just nice to have (at the end of the project) rather than crucial for working software. How to test Java EE applications in a productive yet comprehensive way?

A good test has following criteria: It must be fast, reliable and easy to write and maintain.

There are several ranges of testing like unit, integration or system tests. The smaller the scope of the test is the easier it is to write and to maintain, the faster it runs but also the more “far away” it is from the real world application.

IMO following test scopes should be applied in certain situations.

Unit Tests

Unit tests cover the business logic of a single component — usually a class — and provide very fast feedback for the developers without needing to startup any container.

The simplest way to unit test Java EE components is to use plain JUnit without any special runner and Mockito to mock away every other involved component.

@Stateless 
public class TaskStore { 

  @PersistenceContext 
  EntityManager entityManager; 

  public List<Task> listAll() { 
    return entityManager.createNamedQuery("Task.findAll", Task.class).getResultList(); 
  } 

  public List<Task> filterAll(final Filter filter) { 
    return listAll().parallelStream().filter(filter::matches).collect(Collectors.toList()); 
  } 

  ... 
}


import static org.mockito.Matchers.*; 
import static org.mockito.Mockito.*; 

public class TaskStoreTest { 
  private TaskStore cut; 

  @Before 
  public void setUp() { 
    cut = new TaskStore(); 
    cut.entityManager = mock(EntityManager.class); 

    TypedQuery mockQuery = mock(TypedQuery.class); 
    when(cut.entityManager.createNamedQuery(anyString(), any())).thenReturn(mockQuery); 
    when(mockQuery.getResultList()).thenReturn(sampleTasks()); 
  } 

  @Test 
  public void testFilterContextMatch() { 
    Filter filter = new Filter(); 
    ... 

    List<Task> filteredTasks = cut.filterAll(filter); 
    assertEquals(1, filteredTasks.size()); assertEquals(...); 
    verify(cut.entityManager).createNamedQuery("Task.findAll", Task.class); 
  } 
}


As EJBs and CDI managed beans are basically POJOs with annotations, it’s trivial to create or mock them in a test.

@Injected dependencies can be mocked and manually modified in the class due to the fact that the test resides in the same package as the component and can access the package-private defined property. Not all developers may agree to making the properties not private just for testability. However, this enables the test to easily modify the dependencies without the need for (slow) Reflection. And the Java EE implementation will prevent production code to reset the dependencies of any managed bean.

With that approach one can run hundreds of tests in no time. Any special @RunWith runner definition should be considered over-engineering for a simple unit test — with the exception of Parameterized tests. The latter are in fact very helpful for mass tests.

In general: You can be creative in how to write tests, but they must run fast and simplicity wins.

Integration Tests

Integration tests in a Java EE environment usually mean to combine a few components in a sort of emulated container. Test frameworks like Arquillian can be used to accomplish this.

The problem with this approach is that it consumes much more time than a unit test as the container has to be started every time. That is why it can be a problem to cover business logic with integration tests.

The killer use case however is to test CDI extensions and technical Java EE “plumbing” — rather than business logic. Testing custom CDI producers or custom scopes for example is a perfect scenario. But: These tests should be the big exception in your application.

System Tests

Unit and integration tests are nice to get a fast feedback but if your application is solely tested by them you’ll get other errors on production (like environment-specific configuration, problems with the server environments or external interfaces, etc.). A full system test on the other hand covers all business use cases end-to-end. The application should run in the same way during the test as it would in production. That means the system test environment is ideally the same — including server configuration, databases, network, etc. — with the only difference that it’s not the real production server. During the tests all external systems are mocked away and ensure that the application communicates with them in a correct way.

Having that said means also that integration tests which combine a few components are somewhat overrated. Your application can’t rely on integration tests only and then the feedback time saved is in general not worth the overhead of writing and maintaining them.

That is why I recommend to only have simple unit tests for fast feedback and fully-fledged system tests — with the exception that integration tests sometimes help to test Java EE “plumbing”.

An easy way to achieve an identical system for all environments is to use containers. The container which incorporates the application, the application server and the operating system is built once from the CI server and used for all environments. Using this approach highly increases the stability as exactly the same artefacts run in production which have been tested extensively.

Conclusion

From my point of view a productive and yet comprehensive Java EE testing pipeline looks like follows:

  • Unit tests with plain JUnit (no @RunWiths needed)
  • Integration tests for business logic considered harmful
  • Full system tests which cover all business use cases
  • Continuous Deployment pipeline deploying and starting the system tests automatically
  • Automated performance / stress tests depending on the requirements
  • Deploying the tested application (ideally as container) in production


unit test Java EE Integration Java (programming language) operating system application

Published at DZone with permission of Sebastian Daschner. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Micronaut With Relation Database and...Tests
  • Java CI/CD: From Local Build to Jenkins Continuous Integration
  • A Systematic Approach for Java Software Upgrades
  • Integrate Cucumber in Playwright With Java

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!