ZEMOSO ENGINEERING STUDIO
June 5, 2022
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
How to remix Amazon’s Working Backwards with Google’s Venture’s User Journey: The Dr. Strange Way
How to remix Amazon’s Working Backwards with Google’s Venture’s User Journey: The Dr. Strange Way
June 14, 2022
How we built a big data platform for a futuristic AgriTech product
How we built a big data platform for a futuristic AgriTech product
June 3, 2022
Zemoso Labs starts operations in Waterloo, Canada
Zemoso Labs starts operations in Waterloo, Canada
May 25, 2022
Deconstructing Elon Musk’s dog ate my homework answer for Twitter: More validation will be asked of startups
Deconstructing Elon Musk’s dog ate my homework answer for Twitter: More validation will be asked of startups
May 20, 2022
Real Talk: Lessons learned and evolved from 3M and Post-it®’s adoption of Crazy 8 methodology
Real Talk: Lessons learned and evolved from 3M and Post-it®’s adoption of Crazy 8 methodology
April 10, 2022
Why Realm trumps SQLite as database of choice for complex mobile apps — Part 1
Why Realm trumps SQLite as database of choice for complex mobile apps — Part 1
February 7, 2022
Understanding dynamic multi-column search with JPA Criteria for product development
Understanding dynamic multi-column search with JPA Criteria for product development
January 2, 2022
Product engineering zeitgeist: Implement OOPs in React JS using atomic design
Product engineering zeitgeist: Implement OOPs in React JS using atomic design
July 8, 2021
Honorable mention at O’Reilly’s Architectural Katas event
Honorable mention at O’Reilly’s Architectural Katas event
May 17, 2021
When not to @Autowire in Spring or Spring Boot applications
When not to @Autowire in Spring or Spring Boot applications
May 1, 2021
Efficiently handle data and integrations in Spring Boot
Efficiently handle data and integrations in Spring Boot
January 24, 2021
Unlock the power of atomic design in React and React Native using a theming library — Part 1
Unlock the power of atomic design in React and React Native using a theming library — Part 1
November 2, 2020
Our favorite CI/CD DevOps Practice: Simplify with GitHub Actions
Our favorite CI/CD DevOps Practice: Simplify with GitHub Actions
October 25, 2020
Kubernetes — What is it, what problems does it solve, and how does it compare with alternatives?
Kubernetes — What is it, what problems does it solve, and how does it compare with alternatives?
June 21, 2019
GraphQL — Why is it essential for agile product development?
GraphQL — Why is it essential for agile product development?
April 30, 2019
Orchestrating backend services for product development with AWS Step Functions
Orchestrating backend services for product development with AWS Step Functions
April 1, 2019
How To Decide When to Use Amazon Working Backwards, Business Model Canvas and Lean Canvas
How To Decide When to Use Amazon Working Backwards, Business Model Canvas and Lean Canvas
November 30, 2018
How to validate your Innovation: Mastering Experiment Design
How to validate your Innovation: Mastering Experiment Design
November 22, 2018
Working Backwards: Amazon's Culture of Innovation: My notes
Working Backwards: Amazon's Culture of Innovation: My notes
November 19, 2018
Product developer POV: Caveats when building with Spark
Product developer POV: Caveats when building with Spark
November 5, 2018
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

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

GraphQL — Why is it essential for agile product development?

April 30, 2019
12 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.

© 2021 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
June 5, 2022