ZEMOSO ENGINEERING STUDIO
May 4, 2021
5 min read

Product dev with testable spring boot applications, from day one

As a startup studio, we spend a lot of time cultivating engineering chops that allow us to test smarter. It’s crucial for us to accelerate our client’s deployment efforts. On one hand, test cases add an incredible amount of value to the overall quality of application. On the other, they are tough and can take up a considerable amount of time. However, with the right mechanics and systems in place, they can definitely get easier, even for beginners.

How?

 1. By getting better code coverage.

 2. By creating efficiencies around how long it takes you to write the tests.

 3. By improving the overall test strategy

Some of the most common excuses you might hear for not having a unit test for Spring Boot applications (or for any application) is: “We didn’t have the time to write the tests due to too many changes, not enough time, tight deadlines etc. etc." We say: "No excuses. Test cases are crucial and we’re going to write testable code from day one."


But in order to do that, it’s important to learn when to:

1. Mock

2. Inject

3. Use actual objects

And equally important to understand when to:

1. Run simple JUnit test

2. Run Spring Boot test

3. Run data Java Persistence API (JPA) test

Simple example from with favorite holiday dessert

Consider this simple scenario. A cookie manufacturer makes cookies of the same weight and ships a certain quantity to the dealers. What is the easiest way to count the number of cookies sent to the dealer?

Each cookie weighs x ounces,

Then total cookies in each box will be = box weight / x

Total cookies shipped = shipment weight / x

Total boxes in each shipment = shipment weight / box weight

But, and this one is a big but, this calculation holds true only if, each cookie is the exact same weight. So, as long as the manufacturer makes sure that their testing unit weighs the cookie accurately, they will be able to send out the right number of cookies. They might not even need dedicated cookie counting systems.

The same principle applies to testing of software and code. Once we know what the smallest units of our program are, building out the tests and mocking up smaller units gets easier.

Testing strategy

Covering some basic rules we follow while creating test cases.

1. Simple helper components from Software Development Kits (SDKs): We don’t need dedicated tests. We cover them in other classes.

2. Custom written simple helper components: Dedicated tests are required. Cover them in other classes, too, without mocking them up.

3. Complex and heavy-weight components: Write dedicated tests and then mock the dedicated tests in other classes for complex components of the product build.

4. DAO repositories: Dedicated tests are not required in the case of simple queries. But data Java Persistence API (JPA) tests are essential when using complex queries. This makes sure that the end result doesn’t change unexpectedly.

5. Services: We use dedicated tests for each service and then mock them up in other services. Services may need a combination of spring test and JUnit tests, based on the service. A more detailed look at this is coming up soon.

Now, that we know the breakdown of when to mock and when to use actuals, let me walk you through the testing strategy required for different layers in detail

Helper or data processing components

Simple components

1. Date Formatter

2. String Manipulation

Complex components

1. Components that do heavy calculations

2. Components that make API Calls to other services or establish DB Connections

Writing reliable tests

Step 1 is to understand exactly what the code is supposed to do. At the end of the day, we write tests not to please our managers or get a good SonarQube report, but to ensure that the code works as intended.

Example: Login user function test in auth service

1. New users should be able to login with a valid username and password

2. If the password is not strong, then don’t allow login

3. If the username is taken then, don’t allow login

4. User name does not contain “weird” characters

5. If other credentials like phone or email are already present, then respond with “user already exists”

The four horsemen of testing

Positive testing - Make sure the listed features are working as expected.


Negative testing - Most of the time we figure out negatives while writing down features like an existing username or bad password. But there can be more. Like password length, username length, limited special characters, etc.


Null testing - What if username or password or other required details are null or empty?


Exception testing - Is your code throwing exceptions where required and handling it in other cases?

Mock vs actuals

Mock: Make sure your lower order components are tested correctly before mocking. For example, establishing database connections is a costly operation to build out a test, even within a memory database. Thus, it makes sense to mock it up first, especially when it can be easily done. This gives respective stakeholders and developers a chance to to test it too.


Actual: The actual build can be used for constantly changing components or services which are lightweight, such as the date validator, string formatter, etc. For service, most dependencies in a service can be mocked, including other services. However, if the result of one lightweight service is directly dependent on another service, then we prefer using the actual object instead of a mockup. How to determine that? It comes with years and years of practice and there is, unfortunately, no golden rule.


A thorough understanding of the underlying code, however, goes a long way to aid writing good tests.

But let’s go back to one of the first ‘excuses/objections’ you hear, “The code keeps changing rapidly, how do you approach that?”

Our secret sauce to testing

1. We write the code for the most frequently changing service or component. Never mock it, actually write it. That way, we don’t miss making changes to the codes that are dependent on this constantly changing code, minimizing risk of breaking the code and any last minute surprises.

2. We use actual tests wherever we can. It sounds tedious in the beginning, but as the application starts taking shape, we drastically reduce room for errors, even when adding new features.  

3. We use dedicated tests for each service and then mock them up in other services. Services may need a combination of spring test and JUnit tests, based on the service. A more detailed look at this is coming up soon.

Here is the link to the sample application from the Zemoso author.

A version of this blog post was earlier published on Medium.

ZEMOSO ENGINEERING STUDIO

Product dev with testable spring boot applications, from day one

By Zemoso Engineering Studio
May 4, 2021
5 min read

As a startup studio, we spend a lot of time cultivating engineering chops that allow us to test smarter. It’s crucial for us to accelerate our client’s deployment efforts. On one hand, test cases add an incredible amount of value to the overall quality of application. On the other, they are tough and can take up a considerable amount of time. However, with the right mechanics and systems in place, they can definitely get easier, even for beginners.

How?

 1. By getting better code coverage.

 2. By creating efficiencies around how long it takes you to write the tests.

 3. By improving the overall test strategy

Some of the most common excuses you might hear for not having a unit test for Spring Boot applications (or for any application) is: “We didn’t have the time to write the tests due to too many changes, not enough time, tight deadlines etc. etc." We say: "No excuses. Test cases are crucial and we’re going to write testable code from day one."


But in order to do that, it’s important to learn when to:

1. Mock

2. Inject

3. Use actual objects

And equally important to understand when to:

1. Run simple JUnit test

2. Run Spring Boot test

3. Run data Java Persistence API (JPA) test

Simple example from with favorite holiday dessert

Consider this simple scenario. A cookie manufacturer makes cookies of the same weight and ships a certain quantity to the dealers. What is the easiest way to count the number of cookies sent to the dealer?

Each cookie weighs x ounces,

Then total cookies in each box will be = box weight / x

Total cookies shipped = shipment weight / x

Total boxes in each shipment = shipment weight / box weight

But, and this one is a big but, this calculation holds true only if, each cookie is the exact same weight. So, as long as the manufacturer makes sure that their testing unit weighs the cookie accurately, they will be able to send out the right number of cookies. They might not even need dedicated cookie counting systems.

The same principle applies to testing of software and code. Once we know what the smallest units of our program are, building out the tests and mocking up smaller units gets easier.

Testing strategy

Covering some basic rules we follow while creating test cases.

1. Simple helper components from Software Development Kits (SDKs): We don’t need dedicated tests. We cover them in other classes.

2. Custom written simple helper components: Dedicated tests are required. Cover them in other classes, too, without mocking them up.

3. Complex and heavy-weight components: Write dedicated tests and then mock the dedicated tests in other classes for complex components of the product build.

4. DAO repositories: Dedicated tests are not required in the case of simple queries. But data Java Persistence API (JPA) tests are essential when using complex queries. This makes sure that the end result doesn’t change unexpectedly.

5. Services: We use dedicated tests for each service and then mock them up in other services. Services may need a combination of spring test and JUnit tests, based on the service. A more detailed look at this is coming up soon.

Now, that we know the breakdown of when to mock and when to use actuals, let me walk you through the testing strategy required for different layers in detail

Helper or data processing components

Simple components

1. Date Formatter

2. String Manipulation

Complex components

1. Components that do heavy calculations

2. Components that make API Calls to other services or establish DB Connections

Writing reliable tests

Step 1 is to understand exactly what the code is supposed to do. At the end of the day, we write tests not to please our managers or get a good SonarQube report, but to ensure that the code works as intended.

Example: Login user function test in auth service

1. New users should be able to login with a valid username and password

2. If the password is not strong, then don’t allow login

3. If the username is taken then, don’t allow login

4. User name does not contain “weird” characters

5. If other credentials like phone or email are already present, then respond with “user already exists”

The four horsemen of testing

Positive testing - Make sure the listed features are working as expected.


Negative testing - Most of the time we figure out negatives while writing down features like an existing username or bad password. But there can be more. Like password length, username length, limited special characters, etc.


Null testing - What if username or password or other required details are null or empty?


Exception testing - Is your code throwing exceptions where required and handling it in other cases?

Mock vs actuals

Mock: Make sure your lower order components are tested correctly before mocking. For example, establishing database connections is a costly operation to build out a test, even within a memory database. Thus, it makes sense to mock it up first, especially when it can be easily done. This gives respective stakeholders and developers a chance to to test it too.


Actual: The actual build can be used for constantly changing components or services which are lightweight, such as the date validator, string formatter, etc. For service, most dependencies in a service can be mocked, including other services. However, if the result of one lightweight service is directly dependent on another service, then we prefer using the actual object instead of a mockup. How to determine that? It comes with years and years of practice and there is, unfortunately, no golden rule.


A thorough understanding of the underlying code, however, goes a long way to aid writing good tests.

But let’s go back to one of the first ‘excuses/objections’ you hear, “The code keeps changing rapidly, how do you approach that?”

Our secret sauce to testing

1. We write the code for the most frequently changing service or component. Never mock it, actually write it. That way, we don’t miss making changes to the codes that are dependent on this constantly changing code, minimizing risk of breaking the code and any last minute surprises.

2. We use actual tests wherever we can. It sounds tedious in the beginning, but as the application starts taking shape, we drastically reduce room for errors, even when adding new features.  

3. We use dedicated tests for each service and then mock them up in other services. Services may need a combination of spring test and JUnit tests, based on the service. A more detailed look at this is coming up soon.

Here is the link to the sample application from the Zemoso author.

A version of this blog post was earlier published on Medium.

Recent Publications
Actual access control without getting in the way of actual work: 2023
Actual access control without getting in the way of actual work: 2023
March 13, 2023
Breaking the time barrier: Test Automation and its impact on product launch cycles
Breaking the time barrier: Test Automation and its impact on product launch cycles
January 20, 2023
Product innovation for today and the future! It’s outcome-first, timeboxed, and accountable
Product innovation for today and the future! It’s outcome-first, timeboxed, and accountable
January 9, 2023
From "great potential" purgatory to "actual usage" reality: getting SDKs right in a product-led world
From "great potential" purgatory to "actual usage" reality: getting SDKs right in a product-led world
December 6, 2022
Why Realm trumps SQLite as database of choice for complex mobile apps — Part 2
Why Realm trumps SQLite as database of choice for complex mobile apps — Part 2
October 13, 2022
Testing what doesn’t exist with a Wizard of Oz twist
Testing what doesn’t exist with a Wizard of Oz twist
October 12, 2022
Docs, Guides, Resources: Getting developer microsites right in a product-led world
Docs, Guides, Resources: Getting developer microsites right in a product-led world
September 20, 2022
Beyond methodologies: Five engineering do's for an agile product build
Beyond methodologies: Five engineering do's for an agile product build
September 5, 2022
Actual access control without getting in the way of actual work: 2023
Actual access control without getting in the way of actual work: 2023
March 13, 2023
Breaking the time barrier: Test Automation and its impact on product launch cycles
Breaking the time barrier: Test Automation and its impact on product launch cycles
January 20, 2023
Product innovation for today and the future! It’s outcome-first, timeboxed, and accountable
Product innovation for today and the future! It’s outcome-first, timeboxed, and accountable
January 9, 2023
From "great potential" purgatory to "actual usage" reality: getting SDKs right in a product-led world
From "great potential" purgatory to "actual usage" reality: getting SDKs right in a product-led world
December 6, 2022
Why Realm trumps SQLite as database of choice for complex mobile apps — Part 2
Why Realm trumps SQLite as database of choice for complex mobile apps — Part 2
October 13, 2022
ZEMOSO ENGINEERING STUDIO
May 4, 2021
5 min read

Product dev with testable spring boot applications, from day one

As a startup studio, we spend a lot of time cultivating engineering chops that allow us to test smarter. It’s crucial for us to accelerate our client’s deployment efforts. On one hand, test cases add an incredible amount of value to the overall quality of application. On the other, they are tough and can take up a considerable amount of time. However, with the right mechanics and systems in place, they can definitely get easier, even for beginners.

How?

 1. By getting better code coverage.

 2. By creating efficiencies around how long it takes you to write the tests.

 3. By improving the overall test strategy

Some of the most common excuses you might hear for not having a unit test for Spring Boot applications (or for any application) is: “We didn’t have the time to write the tests due to too many changes, not enough time, tight deadlines etc. etc." We say: "No excuses. Test cases are crucial and we’re going to write testable code from day one."


But in order to do that, it’s important to learn when to:

1. Mock

2. Inject

3. Use actual objects

And equally important to understand when to:

1. Run simple JUnit test

2. Run Spring Boot test

3. Run data Java Persistence API (JPA) test

Simple example from with favorite holiday dessert

Consider this simple scenario. A cookie manufacturer makes cookies of the same weight and ships a certain quantity to the dealers. What is the easiest way to count the number of cookies sent to the dealer?

Each cookie weighs x ounces,

Then total cookies in each box will be = box weight / x

Total cookies shipped = shipment weight / x

Total boxes in each shipment = shipment weight / box weight

But, and this one is a big but, this calculation holds true only if, each cookie is the exact same weight. So, as long as the manufacturer makes sure that their testing unit weighs the cookie accurately, they will be able to send out the right number of cookies. They might not even need dedicated cookie counting systems.

The same principle applies to testing of software and code. Once we know what the smallest units of our program are, building out the tests and mocking up smaller units gets easier.

Testing strategy

Covering some basic rules we follow while creating test cases.

1. Simple helper components from Software Development Kits (SDKs): We don’t need dedicated tests. We cover them in other classes.

2. Custom written simple helper components: Dedicated tests are required. Cover them in other classes, too, without mocking them up.

3. Complex and heavy-weight components: Write dedicated tests and then mock the dedicated tests in other classes for complex components of the product build.

4. DAO repositories: Dedicated tests are not required in the case of simple queries. But data Java Persistence API (JPA) tests are essential when using complex queries. This makes sure that the end result doesn’t change unexpectedly.

5. Services: We use dedicated tests for each service and then mock them up in other services. Services may need a combination of spring test and JUnit tests, based on the service. A more detailed look at this is coming up soon.

Now, that we know the breakdown of when to mock and when to use actuals, let me walk you through the testing strategy required for different layers in detail

Helper or data processing components

Simple components

1. Date Formatter

2. String Manipulation

Complex components

1. Components that do heavy calculations

2. Components that make API Calls to other services or establish DB Connections

Writing reliable tests

Step 1 is to understand exactly what the code is supposed to do. At the end of the day, we write tests not to please our managers or get a good SonarQube report, but to ensure that the code works as intended.

Example: Login user function test in auth service

1. New users should be able to login with a valid username and password

2. If the password is not strong, then don’t allow login

3. If the username is taken then, don’t allow login

4. User name does not contain “weird” characters

5. If other credentials like phone or email are already present, then respond with “user already exists”

The four horsemen of testing

Positive testing - Make sure the listed features are working as expected.


Negative testing - Most of the time we figure out negatives while writing down features like an existing username or bad password. But there can be more. Like password length, username length, limited special characters, etc.


Null testing - What if username or password or other required details are null or empty?


Exception testing - Is your code throwing exceptions where required and handling it in other cases?

Mock vs actuals

Mock: Make sure your lower order components are tested correctly before mocking. For example, establishing database connections is a costly operation to build out a test, even within a memory database. Thus, it makes sense to mock it up first, especially when it can be easily done. This gives respective stakeholders and developers a chance to to test it too.


Actual: The actual build can be used for constantly changing components or services which are lightweight, such as the date validator, string formatter, etc. For service, most dependencies in a service can be mocked, including other services. However, if the result of one lightweight service is directly dependent on another service, then we prefer using the actual object instead of a mockup. How to determine that? It comes with years and years of practice and there is, unfortunately, no golden rule.


A thorough understanding of the underlying code, however, goes a long way to aid writing good tests.

But let’s go back to one of the first ‘excuses/objections’ you hear, “The code keeps changing rapidly, how do you approach that?”

Our secret sauce to testing

1. We write the code for the most frequently changing service or component. Never mock it, actually write it. That way, we don’t miss making changes to the codes that are dependent on this constantly changing code, minimizing risk of breaking the code and any last minute surprises.

2. We use actual tests wherever we can. It sounds tedious in the beginning, but as the application starts taking shape, we drastically reduce room for errors, even when adding new features.  

3. We use dedicated tests for each service and then mock them up in other services. Services may need a combination of spring test and JUnit tests, based on the service. A more detailed look at this is coming up soon.

Here is the link to the sample application from the Zemoso author.

A version of this blog post was earlier published on Medium.

Recent Publications

ZEMOSO ENGINEERING STUDIO

Testing what doesn’t exist with a Wizard of Oz twist

October 12, 2022
7 min read
ZEMOSO ENGINEERING STUDIO

Beyond methodologies: Five engineering do's for an agile product build

September 5, 2022
6 min read
ZEMOSO ENGINEERING STUDIO

How we built a big data platform for a futuristic AgriTech product

June 3, 2022
8 min read
ZEMOSO NEWS

Zemoso Labs starts operations in Waterloo, Canada

May 25, 2022
5 min read
ZEMOSO ENGINEERING STUDIO

Honorable mention at O’Reilly’s Architectural Katas event

May 17, 2021
5 min read
ZEMOSO ENGINEERING STUDIO

When not to @Autowire in Spring or Spring Boot applications

May 1, 2021
5 min read
ZEMOSO ENGINEERING STUDIO

Efficiently handle data and integrations in Spring Boot

January 24, 2021
5 min read
ZEMOSO ENGINEERING STUDIO

Our favorite CI/CD DevOps Practice: Simplify with GitHub Actions

October 25, 2020
5 min read
ZEMOSO ENGINEERING STUDIO

How to use BERT and DNN to build smarter NLP algorithms for products

February 14, 2020
12 min read
ZEMOSO ENGINEERING STUDIO

GraphQL — Why is it essential for agile product development?

April 30, 2019
12 min read
ZEMOSO ENGINEERING STUDIO

GraphQL with Java Spring Boot and Apollo Angular for Agile platforms

April 30, 2019
9 min read
ZEMOSO ENGINEERING STUDIO

Deploying Airflow on Kubernetes 

November 30, 2018
2 min read
ZEMOSO PRODUCT STUDIO

How to validate your Innovation: Mastering Experiment Design

November 22, 2018
8 min read
ZEMOSO PRODUCT STUDIO

Working Backwards: Amazon's Culture of Innovation: My notes

November 19, 2018
8 min read
ZEMOSO ENGINEERING STUDIO

Product developer POV: Caveats when building with Spark

November 5, 2018
2 min read

Want more best practices?

Access thought-leadership and best practice content across
the product development lifecycle

Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

© 2023  Zemoso Technologies
Privacy Policy

Terms of Use
LinkedIn Page - Zemoso TechnologiesFacebook Page - Zemoso TechnologiesTwitter Account - Zemoso Technologies

© 2021 Zemoso Technologies
Privacy Policy

LinkedIn Page - Zemoso TechnologiesFacebook Page - Zemoso TechnologiesTwitter Account - Zemoso Technologies
May 25, 2023