I'm a senior engineer working on backend system design and the architecture of web applications. When I first started working, I understood one thing very quickly, that a robust, resilient, and scalable application needs thorough testing. Like most developers, I started with JUnit to write unit tests. But slowly with time, I started using Cucumber.
When I made the shift, a lot of people from the developer community had warned me that using Cucumber entailed doing more integration tests rather than unit tests. That testing using Cucumber consisted of black box testing, and it would not help in test coverage reporting.
Due to this, I was a little skeptical at first. But as I continued using Cucumber, I grew fond of it more. I will try to list down the reasons why I like it, while addressing some concerns.
Firstly, I accept using Cucumber means more of an integration test rather than a unit. But even so, where’s the harm in that? I generally use this testing technique for REST API. In most cases, they do simple CRUD operations. Rather than testing a specific method with different inputs, I prefer testing the whole business flow by mocking client calls providing the same range of data. And since I use feature files to do so, I can map each test case to a user acceptance criteria.
Now, don’t get me wrong, I do use JUnit also. When there is a specific code that is algorithm heavy, I prefer using writing multiple JUnit tests cases to test it. For example, a method that outputs a reference number depending on a user logged in and the current day of the year. In this scenario, it makes sense to write a JUnit test case to test the algorithm by iterating through a multitude of inputs. (I will share how I test a private method using JUnit in a later post!)
Using Cucumber is not black box testing. You can measure the code coverage. Since you are testing a feature, it touches all the layers and gives you coverage. On the other hand, using JUnit involves writing a lot of tests. This is one of the major reasons why I like using Cucumber. Thus, more coverage is achieved in fewer tests while covering all features and scenarios. We can use Cucumber-spring integration and a JACOCO maven plugin to generate the coverage.
The Cucumber-spring integration can be integrated into the project quite easily just by adding a maven dependency. Thus, all the tests can be executed as part of a maven build. Since the application will be booted before running the test, it also gives confidence to the developer that the spring boot application will boot up without any issues and the application context and dependency injections are behaving the way it should be. This ensures that when a build passes after running test cases, the build deployment is also a success.
Some have concerns that if a feature has integration touchpoints with the upstream or downstream application or database operations, then how can we use Cucumber? Mocks to the rescue! If we are concerned with the database, we can use an in-memory database like H2. Import.sql file placed in the resources folder can be used for seed scripts. Embedded Redis or Hazelcast comes in handy for tests that use a cache. An embedded Redis can be easily added by adding a maven dependency and setting the scope to test. Sometimes, there can even be easier solutions. For example, you can set the cache type to in-memory by adding spring properties such as spring.cache.type=simple. Similarly, Embedded Kafka or ActiveMQ can be used for mocking integrations with an MQ.
If the integration with another system is over REST or SOAP, the communication can be mocked by using WireMock. Loading of spring properties or starting the WireMock server and other embedded servers are part of the test setup, and should be stopped as part of clean up code once the tests have run.
As a summary, I choose to use BDT for REST API based application as it:-
1. Helps me in writing tests in plain English and can be mapped to acceptance criteria
2. Gives me higher code coverage with a lesser number of tests
3. Gives confidence to dev ops by actually testing the loading of the Spring Boot application during the build process, and thus preventing any application load issues after deployment
4. Gives confidence to the developer by actually testing the flow of how an actual user or manual tester does calling the API through a client (browser or mobile etc.)
Before wrapping up, let me again reiterate the points that I have mentioned here are of my own beliefs. If a developer prefers JUnit over Cucumber and is comfortable and confident on his code by using it, he should continue using it as his preferred choice. Whichever weapon we choose, our end goal is the same - a bug-free code.
P.S.:- For more hardcore developers, I will share code and a GitHub URL to share how Cucumber and JACOCO can be used to test a Spring Boot Application in my next blog. Until next time. Have a great day ahead!