Enterprise Java

Testing Web-based Spring Applications in 2013 (Part One)

Testing Web-based Spring Applications in 2013 (Part One)

A fellow LJCer and TDD/BDD advocate has recently started developing an application using the ever-popular Spring stack, and he asked my advice on how best to implement a Test-driven methodology within this context. I started typing an email back in response, but then it suddenly occurred to me that this might be useful for other people as well… So here it is – I hope it helps

Assumptions

The rest of this post assumes the following:

  • Applications will be implemented in Java using the following components of the Spring stack: Core, MVC, Data, Security etc BUT we won’t be covering Spring Integration or Batch (although the recommended testing frameworks and tools can be quite similar I find that the techniques for testing these components can be quite different, and so this will have to wait until another blog post)
  • ‘End-users’ will interact with the developed application(s) via a web-based UI and/or HTTP-based API (typically, but not exclusively REST-like)
  • For production an external datastore will be utilised to persist long-lived state within the application. We assume that you will want to test how your application interacts with this too.
  • We will not cover testing via remote or embedded containers, other than using a simple in-memory servlet container such as Jetty to deploy the entire packaged application. Frameworks such as Arquillian offer some really cool features that allow Unit testing against real (embedded) containers such as Tomcat, GlassFish and JBoss Application Server. This is achieved by providing mechanisms to simulate production-like packaging (“ShrinkWrapping“) and deployment of an arbitrary sub-set of components (down to the Class-level) for testing within the embedded container, all via your favourite testing framework such as JUnit.

General Recommendations

If you’re new test-driven development I recommend the following books:

  • Effective Unit Testing: A guide for Java developers – This is a great (and up to date) introduction to all key concepts and technologies within Java-based unit testing. This book not only covers motivations and methodologies, but also how to write ‘good’ tests that are expressive and maintainable etc, and also advice on choosing the most appropriate technique for each use case.
  • Practical Unit Testing with JUnit and Mockito and/or Practical Unit Testing with TestNG and Mockito. These are both great books that contain a wealth of practical examples on how to test Java applications, and also discusses the why and how of TDD. Both books are very similar and differ primarily in the framework used to drive the tests. My advice is to buy the TestNG version in combination with ‘Effective Unit Testing’ (which is primarily focused on JUnit), as it’s great to know the differences and strengths/weaknesses of the two most popular Java-based testing engines.
  • Growing Object-Oriented Software, Guided by Tests (Beck Signature) – this is one of the classic TDD books, and it discusses all of the high-level concepts and motivations of Test-driving application design. This book also provides a great practical example of how to use the prescribed methodology by detailing the construction and evolution of an entire application based on an auction website.
  • There are a few books written about Unit testing JavaScript, and although I haven’t had chance to read them all I do recommend Testable JavaScript

Testing the Core

Unit Testing

Purpose: The core of your application models the problem you are trying to solve, and typically contains domain-specific representations and associated ‘business’ rules which you are primarily responsible for writing (i.e. the opportunity to re-use third-party code here is somewhat limited).

Accordingly it is easy to argue that this code should be well tested – not only are the components you implement here likely to be unique, but you want to be sure they behave as expected before wiring them up to frameworks that offer boilerplate functionality, such as persistence. Well written tests here can also ’document’ your code (and associated functionality) at the micro level.

Tools:

  • JUnit / TestNG – de facto frameworks for running Java tests. I like to run these as part of my Maven build process via the surefire plugin
  • Spock – an awesome Groovy-based testing and specification framework. If you haven’t heard of it stop reading right now and go visit the webpage. Seriously. Now check out the Spock Basics page. How awesome is that??  And it plays well with Spring too!
  • Mockito – my favourite mocking framework. Others do exist, but I find Mockito strikes a great balance betweeen ease of use, expressivity and maintainability
  • PowerMock – a great extension to Mockito for use when you face a difficult situation that Mockito can’t cope with, such as mocking an old library that relies on static methods, or mocking private or final methods, or mocking object instantiation. My advice is to always double and triple-check your component design when you start reaching for PowerMock – often the desire to use PowerMock indicates a testing ‘smell’, and may indicate that your component design should be improved, or the manner in which your code interfaces with external dependency could be enhanced. For example, rather than mocking a third-party library’s static constructor method, could you not hide this method call behind your own interface (following the decoractor/adapter pattern) which will facilitate easier mocking?
  • ConcurrentUnit – a great utility framework for the difficult task of testing concurrency within components
  • Make-it-easy – a great little framework for creating Test Data builder (the concept of which comes from the aforementioned ‘Growing Object-Oriented Software, Guided by Tests)
  • junitparams – a great framework for augmenting JUnits parameterised testing (or as the creator states – ‘Parameterized tests that don’t suck’)

Do:

  • Isolate Classes and components under test. Unit tests should be focused on testing small units of work, and therefore have minimal dependencies (or dependencies should be mocked)
  • Create test cases that cover all paths through the code, not just the happy path
  • Create tests the replicate edge cases
  • Use the power of parameterised tests to facilitate rapid addition of data-driven test cases as they are discovered

Don’t:

  • Write trivial tests just to increase unit test coverage
  • Create brittle tests that break with the slightest change to your code i.e. if your using more than 10 lines of code to set up your mocks and expectations then you may be doing something wrong
  • Test getters and setters, unless they contain logic
  • Directly test any interaction with the underlying OS (file, network etc), a datastore, or any container/server. This interaction should be mocked at this level of testing

Plugging it together

Integration Tests – Persistence Layer

Purpose: Think Unit testing for your persistence or DAO layer, but here you want to simulate behaviour offered by an external resource, rather than mocking this functionality.

Tools:

  • All the Unit Testing tools mentioned above plus…
  • Utilise Spring’s @ContextConfiguration  to manage the wiring of dependencies together, such as an EntityManager or MongoOperations, and AbstractTransactionalJUnit4SpringContextTests to manage running the tests with well-defined transaction semantics
  • Embedded (in-memory) versions of your chosen datastore e.g.
    • H2 – A great embedded SQL datastore, and in 99% of standard use cases behaves like MySQL (although Your Mileage May Vary…)
    • Embedded MongoDB – for all your MongoDB datastore needs (or for older versions of MongoDB, pre 2.9, check out another project I contributed to SDFongo)
    • Embedded Solr via ZoomInfo’s InProcessSolrServer, and updated by me (shameless self-plug! )
    • ActiveMQ – run as embedded and non-persisting

Do:

  • Assert that entities persist as you expect them. I’ve lost count of the number of times I’ve incorrectly configured XToMany JPA annotations which resulted in incorrect cascading operations to child entities.
  • Aim to keep test execution time reasonably small.
  • Test all public methods exposed on the DAO/repository interface
  • Create harnesses that allow the loading of pre-canned test data. Think executeSqlScript() from Spring’s AbstractTransactionalJUnit4SpringContextTests
  • Destroy (or delete) data at the end of each test in order to prevent previous test data from influencing results. This is especially important if your chosen testing framework doesn’t guarantee the order in which tests are executed!

Don’t

  • Overly duplicate coverage from the Unit tests i.e. most business logic will call the persistence layer to load data, process this, and call the persistence layer again to save the results. Tests at this level only need to focus on the load and save
  • Test every conceivable permutation of data
  • Chain too many operations together – this should be covered by service-integration tests or end-to-end tests

Coming in Part 2…

I’ll try and post Part 2 soon, and this will cover topics such as Service-level Integration testing, Web-based testing and API testing. Part 3 will cover end-to-end (E2E) testing and BDD.
 

Daniel Bryant

Daniel Bryant spends his day working as a software development consultant, specialising in designing and deploying JVM and NoSQL-based business solutions to the cloud for Small-to-Medium Enterprises. By night he works on several open-source projects, primarily with the goal to contribute back to the community, but also to learn about exciting new technologies. Daniel is an active member of the London Java Community and as part of this role he is currently working on the 'Adopt OpenJDK' Betterrev project. This project strives to make contributions to the OpenJDK source code much simpler, with the ultimate goal of promoting community engagement in the Reference Implementation for Java. During any remaining spare time Daniel also keeps an eye on academic developments within Artificial Intelligence and defeasible reasoning, having gained a PhD in this topic during his misspent youth as an academic.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

4 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
anujgandharv
10 years ago

Maybe you want to have a look at EasyTest Framework https://github.com/EaseTech/easytest-core
This is a Data Driven Testing Framework built on top of JUnit and allows a lot of kewl new features for writing sleak Unit/Integration tests. It also has a EasyTest-Spring Integration lib : https://github.com/EaseTech/easytest-spring
which makes Spring based Integration tests Data Driven.

Suraj
Suraj
10 years ago

Can you share some examples too?

Marvo
10 years ago

“Destroy (or delete) data at the end of each test in order to prevent previous test data from influencing results. This is especially important if your chosen testing framework doesn’t guarantee the order in which tests are executed!” Actually, we do this BEFORE running tests. That way, you KNOW the data is gone as the test starts. If a test fails, it may not clean up after itself (for instance, if you kill it in the debugger.) This will save you a LOT of time. While I know there are tools to do this sort of thing for you, with… Read more »

Binh Thanh Nguyen
Binh Thanh Nguyen
9 years ago

Thanks, nice tips.

Back to top button