Friday, September 26, 2025

Least Common Multiple (LCM) of Two Numbers - Java Program

In this post we'll see how to write a Java program to calculate LCM (Least Common Multiple) of two numbers.

The least common multiple (LCM) is the smallest positive integer that is a multiple of two or more given numbers (perfectly divisible by the numbers).

For example, LCM of 14 and 16 is-

14 = 2 X 7
16 = 2 X 2 X 2 X 2
LCM = 2 X 2 X 2 X 2 X 7 = 112

Java program to calculate LCM of two numbers can be written using following approaches-

  1. Brute force approach to find the common multiple
  2. Using prime factorization to find LCM
  3. Using GCD of two numbers to find LCM

1. LCM of two numbers using brute force approach - Java Program

In this iterative approach we go by the fact that the lower bound for the LCM of two positive integers is the greater of the two. For example, if numbers are 12 and 16 then the LCM won't be less than 16 at least.

If two numbers are and b then the logic is to divide the max of and b by both a and b until the number is completely divisible by both a and b. In each iteration keep incrementing max by 1.

while(true) {
  if(max % num1 == 0 && max % num2==0) {
    break;
  }
  max++;
}

Here is the complete Java program-

public class LCMProgram {

  public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
    System.out.println("Enter first number: ");
    int num1 = sc.nextInt();
    
    System.out.println("Enter second number: ");
    int num2 = sc.nextInt();
    
    int lcm = calculateLCM(num1, num2);
    System.out.println("LCM is " + lcm);
    sc.close();
  }
  
  public static int calculateLCM(int num1, int num2) {
    if(num1 == 0 || num2 == 0) {
      return 0;
    }
    int max = num1 > num2 ? num1 : num2;
    while(true) {
      if(max % num1 == 0 && max % num2==0) {
        break;
      }
      max++;
    }
    return max;
  }
}

Output

Enter first number: 
12
Enter second number: 
16
LCM is 48
Enter first number: 
0
Enter second number: 
5
LCM is 0
Enter first number: 
10
Enter second number: 
30
LCM is 30

2. Using prime factorization to find LCM - Java Program

1. In this approach as first step find prime factors of each number. For example, if numbers are 14 and 16 then-

14 = 2 X 7 = 21 X 71
16 = 2 X 2 X 2 X 2 = 24

2.Identify the highest power of each prime factor. As per our example we have two distinct prime factors 2 and 7 with highest power as-

For 2 it is - 24
For 7 it is - 71

3. Multiply the highest powers of each prime factors together to get the LCM

24 X 71 = 16 X 7 = 112

Java program for this approach can be written using HashMap where key is the factor and value is its power. For two numbers we'll have two maps with these entries.

Then for each key find the max power among all the maps.

Here is the complete Java program.

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;

public class LCMProgram {

  public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
    System.out.println("Enter first number: ");
    int num1 = sc.nextInt();
    
    System.out.println("Enter second number: ");
    int num2 = sc.nextInt();
    
    int lcm = calculateLCM(num1, num2);
    System.out.println("LCM is " + lcm);
    sc.close();
  }
  
  public static int calculateLCM(int num1, int num2) {
    if(num1 == 0 || num2 == 0) {
      return 0;
    }
    Map<Integer, Integer> factorsNum1 = getPrimeFactors(num1);
    Map<Integer, Integer> factorsNum2 = getPrimeFactors(num2);
    Set<Integer> allFactors = new HashSet<>();
    // add keys in a Hashset to get distinct factors
    allFactors.addAll(factorsNum1.keySet());
    allFactors.addAll(factorsNum2.keySet());
    int lcm = 1;
    for(int factor: allFactors) {
      // Look for the highest power for each factor
      lcm *= Math.pow(factor, Math.max(factorsNum1.getOrDefault(factor, 0), factorsNum2.getOrDefault(factor, 0)));
    }

    return lcm;
  }

  
  private static Map<Integer, Integer> getPrimeFactors(int number){
    Map<Integer, Integer> factors = new HashMap<>();
    for(int i = 2; i< number/2; i++) {
      while(number%i == 0) {
        factors.put(i, factors.getOrDefault(i, 0)+1);            
        number = number/i;
      }
    }
    // if number is not yet completely divisible, then it's a prime factor itself
    if(number >2) {
      factors.put(number, factors.getOrDefault(number, 0)+1);            
      
    }
    return factors;
  }
}

Output

Enter first number: 
15
Enter second number: 
25
LCM is 75

3. Using GCD of two numbers to find LCM - Java Program

Relationship between the two numbers for which LCM is calculated and their GCD is that the product of two numbers is equal to the product of LCM and GCD of those numbers.

For example, if a and b are two positive integers then

a * b = LCM(a, b) * GCD(a,b)

That means-

LCM(a, b) = (a*b) / GCD(a, b)

Refer Greatest Common Divisor (GCD) of Two Numbers Java Program to see different approaches for finding GCD of two numbers.

Here is the Java program for this approach-
public class LCMProgram {

  public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
    System.out.println("Enter first number: ");
    int num1 = sc.nextInt();
    
    System.out.println("Enter second number: ");
    int num2 = sc.nextInt();
    
    int lcm = calculateLCM(num1, num2);
    System.out.println("LCM is " + lcm);
    sc.close();
  }

  
  public static int calculateLCM(int num1, int num2) {
    if(num1 == 0 || num2 == 0) {
      return 0;
    }
    int gcd = gcdEuclidDivision(num1, num2);
    int lcm = (num1*num2)/gcd;
    return lcm;
  }
  
  
  public static int gcdEuclidDivision(int a, int b) {
    int temp;
    // Find the maximum of two numbers, which is assigned to a
    if(a < b) {      
      temp = a;
      a = b;
      b = temp;
    }
    while(b != 0) {
      temp = b;
      b = a % b;
      a = temp;
    }
    return a;
  }
  
}

Output

Enter first number: 
15
Enter second number: 
25

LCM is 75

That's all for this topic Least Common Multiple (LCM) of Two Numbers - Java Program. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Java Programs Page


Related Topics

  1. Java Program to Display Prime Numbers
  2. Fibonacci Series Program in Java
  3. How to Display Pyramid Patterns in Java - Part1
  4. Convert String to int in Java
  5. Converting String to Enum Type in Java

You may also like-

  1. Shell Sort Program in Java
  2. Binary Search Program in Java
  3. Manacher's Algorithm to Find The Longest Palindrome - Java Program
  4. Find Duplicate Elements in an Array Java Program
  5. Spring Data JPA - Spring Data Tutorial
  6. Angular One-Way Data Binding Using String Interpolation
  7. Python Conditional Statement - if, elif, else Statements
  8. JavaScript filter Method With Examples

Manacher's Algorithm to Find The Longest Palindrome - Java Program

Manacher's algorithm is used to find the longest palindromic substring in a given string in linear time. There are other solutions to find the longest palindromic substring in a given string ranging from O(n2) to O(n log n) but Manacher's algorithm tries to do it in O(n) time.

How does Manacher's algorithm work

Manacher's Algorithm uses the combination of symmetry and center expansion to solve the problem.

  1. Symmetry- Palindromes are inherently symmetrical around their center. If center is c and string s is a palindrome of length 7 then the sub-string (3 characters) on the right side of c is a mirror image of sub-string (3 characters) on the left side of c. With in a given boundary Manacher’s algorithm tries to look for other potential palindromes using this symmetry in an optimized way which avoids redundancy. Will try to explain this point with the help of few examples afterwards.
  2. Center Expansion- It is the process of expanding in both directions from a center character, one character at a time on both left and right side and checking if both characters are equal. If yes, then it adds to the palindrome.

Another optimization in Manacher's algorithm is to transform the given string by inserting special characters (like "#") between each character in the String so that string is always odd in length. That way you don't need separate logic for odd length and even length strings.

For the string "1221", the transformed string becomes "#1#2#2#1#". So, from length 4 it transforms to string of length 9. For the string of length n length of the transformed string is (2n+1).

Manacher's algorithm - Java Program

We need to initialize an array LPS to store length of the palindrome around each value of i. Also, variables center and right to center and right boundary of the current longest palindrome.

Transform the string by inserting character ("#") between each character in the String.

Traverse the transformed string and for each i (where 0 <= i < transformedstring.length)

  • Calculate the mirror position using 2 * center – i
  • Check if (right > i) then set lps[i] = Math.min(lps[mirror], right-i);
    This is the optimization in the Manacher’s algorithm to reuse the previous palindrome information from the mirror position.
  • Compare the characters around i to check if the palindrome can be further expanded.
  • If there is an expansion then reposition the center and right to reflect the current palindrome length.
    center = i;
    right = i + lps[i];
    
import java.util.Arrays;

public class Manacher {

  public static void main(String[] args) {
    Manacher m = new Manacher();
    String s = "pqpypqpypqq";
    //String s = "pppqpyp";
    //String s = "9121312187";
    String str = m.longestPalindrome(s);
    System.out.println(str);

  }
  
  public String longestPalindrome(String s) {
    int n = s.length();
    char[] newStr = new char[2*n+1];
    int p = 0;
    int[] lps = new int[2*n+1]; 
    int maxLength= 0;
    int maxCenter = 0;
    // modify string to ensure it is always of odd length
    for(int i = 0; i < 2*n; i++) {
      newStr[i++] = '#';
      newStr[i] = s.charAt(p++);
    }
    newStr[2*n] = '#';
    
    System.out.println("Transformed String is " + Arrays.toString(newStr));
    
    int center=0, right = 0;
    for(int i = 0; i < newStr.length; i++) {
      int mirror = 2 * center - i;
      System.out.println("LPS array " + Arrays.toString(lps));

      // this is the optimization in Manacher's algorithm, 
      // To check with in the right boundary if there is any 
      // other palindrome by looking into mirror part of it
      if(right > i) {      
        lps[i] = Math.min(lps[mirror], right-i);
      }
      int r = i + (lps[i]+1);
      int l = i - (lps[i]+1);
      while(l >= 0 && r < newStr.length && newStr[r]==newStr[l]) {
        l--;
        r++;
        lps[i]++;
      }
      if(lps[i] >= maxLength) {
        maxCenter = i;
        maxLength = lps[i];
      }
      //with comparisons in while loop we have a palindrome 
      // going beyond right boundary so extend right
      if(i + lps[i] > right) {
        center = i;
        right = i + lps[i];
      }
    }
    // convert char array to string
    String st = new String(newStr);
    return st.substring(maxCenter - maxLength, maxCenter + maxLength).replace("#", "");
  }
}

Output

Transformed String is [#, p, #, q, #, p, #, y, #, p, #, q, #, p, #, y, #, p, #, q, #, q, #]
LPS array [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
LPS array [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
LPS array [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
LPS array [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
LPS array [0, 1, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
LPS array [0, 1, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
LPS array [0, 1, 0, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
LPS array [0, 1, 0, 3, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
LPS array [0, 1, 0, 3, 0, 1, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
LPS array [0, 1, 0, 3, 0, 1, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
LPS array [0, 1, 0, 3, 0, 1, 0, 7, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
LPS array [0, 1, 0, 3, 0, 1, 0, 7, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
LPS array [0, 1, 0, 3, 0, 1, 0, 7, 0, 1, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
LPS array [0, 1, 0, 3, 0, 1, 0, 7, 0, 1, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
LPS array [0, 1, 0, 3, 0, 1, 0, 7, 0, 1, 0, 9, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
LPS array [0, 1, 0, 3, 0, 1, 0, 7, 0, 1, 0, 9, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
LPS array [0, 1, 0, 3, 0, 1, 0, 7, 0, 1, 0, 9, 0, 1, 0, 5, 0, 0, 0, 0, 0, 0, 0]
LPS array [0, 1, 0, 3, 0, 1, 0, 7, 0, 1, 0, 9, 0, 1, 0, 5, 0, 0, 0, 0, 0, 0, 0]
LPS array [0, 1, 0, 3, 0, 1, 0, 7, 0, 1, 0, 9, 0, 1, 0, 5, 0, 1, 0, 0, 0, 0, 0]
LPS array [0, 1, 0, 3, 0, 1, 0, 7, 0, 1, 0, 9, 0, 1, 0, 5, 0, 1, 0, 0, 0, 0, 0]
LPS array [0, 1, 0, 3, 0, 1, 0, 7, 0, 1, 0, 9, 0, 1, 0, 5, 0, 1, 0, 1, 0, 0, 0]
LPS array [0, 1, 0, 3, 0, 1, 0, 7, 0, 1, 0, 9, 0, 1, 0, 5, 0, 1, 0, 1, 2, 0, 0]
LPS array [0, 1, 0, 3, 0, 1, 0, 7, 0, 1, 0, 9, 0, 1, 0, 5, 0, 1, 0, 1, 2, 1, 0]
qpypqpypq

As stated above,

if(right > i) {      
  lps[i] = Math.min(lps[mirror], right-i);
}

This code is the main optimization with in the Manacher's algorithm to reuse the palindrome information around the mirror position of the current index to reduce the number of comparisons.

To understand it better let's take the example of String as "pqpypqpypqq"

After transforming it the transformed String is

"#p#q#p#y#p#q#p#y#p#q#q#"

And intial LPS array is [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

Now let's take the scenario when i = 4, right = 6 and center = 3 so there is a palindrome with center as 3 and stretching till right.

Manacher's algorithm Java program

And at that time LPS array would have values as shown here-

LPS array [0, 1, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

Since right > i, so we need to check if there is any other palindrome with in the right boundary that can be reused. For that we have to get the mirror position of i which is 2.

As you can see characters at index 1 and 3 don't match and that same info is reflected in LPS[2] which is 0. That means there is no palindrome to reuse and check for palindrome around i (which is 4) has to start from

int r = i + (lps[i]+1) = 5
int l = i - (lps[i]+1) = 3

When i = 5, right = 6 and center = 3, at that time mirror position of i is 1.

As you can see, at mirror position 1 there is a palindrome of length 1 around mirror index and that same info is reflected in LPS[1] which is 1. So, we can safely deduce that index 0..2 will be same as index 4..6 and that information can be used. So, the check for palindrome around i (which is 5) has to start from

int r = i + (lps[i]+1) = 7
int l = i - (lps[i]+1) = 3

If we skip to the scenario when i = 11, right = 14 and center = 7 with mirror position of i as 3.

At that time LPS array will look like-

[0, 1, 0, 3, 0, 1, 0, 7, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

As you can see at mirror position 3 there is a palindrome of length 3 around mirror index and that same info is reflected in LPS[3] which is 3. So, we can safely deduce that index 0..6 will be same as index 8..14 and that information can be used. We never go beyond the right boundary while trying to see what can be reused because we are only sure of the values that are with in the range that is already checked not beyond that.

In this scenario we can start character comparison on the left and right side using the following indices.

int r = i + (lps[i]+1) = 15
int l = i - (lps[i]+1) = 7

Time and space complexity of Manacher's algorithm

There is a pre-processing step to transform the string taking O(n) time.

In the program, with in the for loop, there is an inner while loop too, so it may seem that the time complexity should be O(n2) but the thing is with each matching comparison right also moves one step forward and it is never decreased. That means right also traverse from 0 to n. Therefore, the inner while loop doesn't get executed more than n times.

Then the substring operation to get the final result can also be termed as O(n) operation. Thus, the overall time complexity is O(n), removing any constants.

An array is needed to keep tracks of palindrome lengths around each index. Thus the space complexity of Manacher's algorithm is O(n).

That's all for this topic Manacher's Algorithm to Find The Longest Palindrome - Java Program. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Java Programs Page


Related Topics

  1. Check if Given String or Number is a Palindrome Java Program
  2. Reverse Each Word in a String Java Program
  3. Find All Permutations of a Given String Java Program
  4. Java Program to Find The Longest Palindrome in a Given String
  5. Print Odd-Even Numbers Using Threads And Semaphore - Java Program

You may also like-

  1. How to Format Date in Java Using SimpleDateFormat
  2. How to Read File From The Last Line in Java
  3. How to Create PDF in Java Using OpenPDF
  4. Callable and Future in Java concurrency
  5. Java ReentrantLock With Examples
  6. Dependency Injection in Spring Framework
  7. Spring Component Scan to Automatically Discover Beans

Java Program to Find The Longest Palindrome in a Given String

This post is about writing a Java program to find the longest palindrome in a given String.

Logic for finding the longest palindrome in a given string

The solution given here works on the logic that in a palindrome, starting from the center, if two cursors are moved left and right respectively one character at a time, those values should be equal. This holds true if the length of the string is odd.

For example, if string is 121 then centre is 2. Character at the left and character at the right, if checked for equality should be equal. You can check for 24642, aba, malayalam.

If the length of the string is even then you have to take 2 characters as center, and then check the character at the left and character at the right for equality. Of course two characters considered as center should be equal too.

For example, if string is 1221, then centre is 22 from there you move one character to left and one character to right. You can check for toppot, abba.

Punctuation, capitalization, and spaces are usually ignored, in the given code it is not done though.

Note that this Java program is to find the longest palindrome in the given string. For example- bananas, in this string "ana", "ana" and "anana" three palindromes are present but the longest is "anana".

If you are looking for Java program to find whether given string is palindrome or not refer this link- Check if Given String or Number is a Palindrome Java Program

Java code for finding the longest palindromic String

public class PalDemo {

  public static void main(String[] args) {
    PalDemo pd = new PalDemo();
    
    String pal = pd.findLongestPalindrome("bananas");
    System.out.println("" + pal);
    
    pal = pd.findLongestPalindrome("abaradar121");
    System.out.println("" + pal);
  }
    
  public String findLongestPalindrome(String s) {
    // Validations
    if (s.isEmpty()) {
      return "Please enter a String";
    }

    if (s.length() == 1) {
      return s;
    }
    // Validations end
    // Start with one char (starting) as a longest palindrome
    String longest = s.substring(0, 1);
    for (int i = 0; i < s.length(); i = i+1) {        
      // get longest palindrome for odd length (center is i)
      String tmp = checkForEquality(s, i, i);
      if (tmp.length() > longest.length()) {
        longest = tmp;
      }

      // get longest palindrome for even length (center is i, i+1)
      tmp = checkForEquality(s, i, i + 1);
      if (tmp.length() > longest.length()) {
        longest = tmp;
      }
    }
    return longest;
  }
    
    
  /**
  * In this method equality is checked starting from
  * the center moving one character left and one character
  * right from the center. If both chars are equal then the
  * next set of chars are checked.  
  *     
  */
  public String checkForEquality(String s, int begin, int end) {
    while (begin >= 0 && end <= s.length() - 1 && s.charAt(begin) == s.charAt(end)) {
      begin--;
      end++;
    }
    return s.substring(begin + 1, end);    
  }
}
 

Output

anana
radar

Let's try to have a dry run with 121 as the entered string and trace the steps-

  1. After checking if String is empty or having just one character, first character of the string is stored as the longest.
  2. From the for loop, in the first call to method checkForEquality() entered String is passed as the first param. Other two params begin and end will be 0 and 0.
  3. In the while loop in the method checkForEquality(), begin >= 0 && end <= s.length() - 1 condition will pass as begin = 0 and end is less than 2 (length of string – 1). s.charAt(begin) == s.charAt(end) condition will also pass as both begin and end are pointing to same char. So begin has a value -1 and end has a value 1 now. With that while loop will fail.
    Only first char of the string will be returned as s.substring(begin + 1, end) will be translated as s.substring(0, 1) for begin = -1 and end = 1.
  4. Again checkForEquality() method will be called with 0 and 1 (this is to check for even case). With these values while loop will fail for the condition s.charAt(begin) == s.charAt(end) as both values will be different.
  5. Now i is 1, in that case s.charAt(begin) == s.charAt(end) condition will pass as value will be 2 for both. So begin-- gives begin = 0 and end++ gives end = 2. Again s.charAt(begin) == s.charAt(end) will pass as value will be 1 for both. So begin-- gives begin = -1 and end++ gives end = 3. With these values it will come out of while loop and returns s.substring(0, 3) which is 121.
  6. Since this returned value's length is greater than the current longest string so returned value becomes the longest.

Time complexity of the solution

Program given here to find the longest palindrome in a string in Java has a time complexity of O(N2), there is also a linear solution known as Manacher's algorithm

That's all for this topic Java Program to Find The Longest Palindrome in a Given String. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Java Programs Page


Related Topics

  1. Check if Given String or Number is a Palindrome Java Program
  2. Count Number of Words in a String Java Program
  3. Add Double Quotes to a String Java Program
  4. Convert String to int in Java
  5. Print Odd-Even Numbers Using Threads And Semaphore - Java Program

You may also like-

  1. How to Format Date in Java Using SimpleDateFormat
  2. How to Read File From The Last Line in Java
  3. Abstraction in Java
  4. Race Condition in Java Multi-Threading
  5. Callable and Future in Java concurrency
  6. Java ReentrantLock With Examples
  7. Dependency Injection in Spring Framework
  8. Spring Component Scan to Automatically Discover Beans

Spring Boot REST API Documentation - OpenAPI, Swagger

In this article we'll see how to integrate Spring Boot application with Open API to generate REST API documentation based on Open API specification.

springdoc-openapi library

springdoc-openapi Java library provides integration of Spring Boot application with Open API and Swagger. This library automates the generation of API documentation as per Open API 3 specification. springdoc-openapi works by examining an application at runtime to infer API semantics based on spring configurations, class structure and various annotations.

This library supports:

  • OpenAPI 3
  • Spring-boot v3
  • JSR-303 (Bean Validation)
  • Swagger-ui
  • OAuth 2
  • GraalVM native images

What you get with springdoc-openapi library

  1. Automatically generates documentation in JSON/YAML and HTML format APIs.
  2. You can enhance the automatically generated documentation by using Swagger-API annotations (e.g., @Tag, @Operation, @ApiResponse, @Parameter)
  3. springdoc-openapi provides various modules to support different Spring technologies and features-
  4. Spring WebMvc support through springdoc-openapi-starter-webmvc-ui (for both OpenAPI description and swagger-ui), springdoc-openapi-starter-webmvc-api (if you need only OpenAPI description).
  5. Spring WebFlux support through springdoc-openapi-starter-webflux-ui (for both OpenAPI description and swagger-ui), springdoc-openapi-starter-webflux-api (if you need only OpenAPI description).
  6. Spring Hateoas support- The support for Spring Hateoas is available using the dependency springdoc-openapi-hateoas.
  7. Spring Data Rest support- springdoc-openapi project supports spring-boot-starter-data-rest types like: @RepositoryRestResource and QuerydslPredicate annotations.
  8. Provides support for spring-security, Kotlin, Groovy, Javadoc.

Spring Boot with Open API and Swagger example

Please refer this post- Spring Boot + Data JPA + MySQL REST API CRUD Example to see the Controller class for which we are going to write unit tests.

UserController.java

Controller class is given here also for easy understanding.

@RestController
@RequestMapping("/user")
public class UserController {
  
  private final UserService userService;
  UserController(UserService userService){
    this.userService = userService;
  }
  
  // Insert user record
  @PostMapping
  public ResponseEntity<User> addUser(@Valid @RequestBody User user) {
    User createdUser = userService.addUser(user);
    return ResponseEntity.status(HttpStatus.CREATED).body(createdUser);
  }
  
  // Fetch all user records
  @GetMapping("/allusers")
  public ResponseEntity<List<User>> getAllUsers(){
    return ResponseEntity.ok(userService.getAllUsers());
  }
  
  // Fetch single user
  @GetMapping("/{id}")
  public ResponseEntity<User> getUserById(@PathVariable("id") int userId){
    //System.out.println("ID to find " + userId);
    User user = userService.getUserById(userId);
      return ResponseEntity.ok(user);
  }
  
  // Update user record
  @PutMapping
  public ResponseEntity<User> updateUser(@RequestBody User user) {
    System.out.println(user);
    User updatedUser = userService.updateUser(user);
    return new ResponseEntity<User>(updatedUser, HttpStatus.OK);
    
  }
  
  // Delete user record
  @DeleteMapping("/{id}")
  public ResponseEntity<Void> deleteUser(@PathVariable int id){
      userService.deleteUserById(id);
      return ResponseEntity.noContent().build();
      //return new ResponseEntity<String>("User deleted successfully", HttpStatus.OK);   
  }
  
  @GetMapping("/type")
  ResponseEntity<?> getUserByType(@RequestParam("type") String userType){

    System.out.println("userType " + userType);
    List<User> users = userService.getAllUsersByUserType(userType);
    if(users.isEmpty()) {
      return ResponseEntity.noContent().build();
    }
    return ResponseEntity.ok(users);
    
  }
}

springdoc-openapi dependency

First thing is to add the springdoc-openapi-starter-webmvc-ui starter for the integration between spring-boot and swagger-ui. Add the following to your pom.xml if you are using Maven

<dependency>
  <groupId>org.springdoc</groupId>
  <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
  <version>2.8.12</version>
</dependency>

If you are using Gradle then

implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui: 2.8.12")

Please check the version as per your Spring Boot version.

That's all you need to generate API documentation. You can access the given endpoints.

  1. Endpoint for swagger UI page is- /swagger-ui.html
  2. Endpoint for OpenAPI description (in JSON format)- v3/api-docs

With Swagger UI you can see how to send requests for different methods in controller classes and what do you get back as response. You can even test the methods from Swagger UI.

On accessing http://localhost:8080/swagger-ui/index.html

Spring Boot Swagger UI

As you can see in the documentation for PUT mapping, it shows how request should look like, what will be the response format. You can test it too by clicking on "Try it out" button.

Access http://localhost:8080/v3/api-docs to get the API documentation in JSON format.

Spring Boot Open API

In the Swagger UI, you can also see the schema part where validations are also documented. User class is displayed here and you can see that the firstName and lastName are marked with @NotBlank annotation.

public class User {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(name = "ID")
  private int userId;
  
  @NotBlank(message = "First name is required")
  @Column(name="FIRST_NAME")
  private String firstName;
  
  @NotBlank(message = "Last name is required")
  @Column(name="LAST_NAME")
  private String lastName;
  …
 …
}

In the schemas section you can see that the validation information is documented.

OpenAPI Scehmas

Open API Configuration

You can provide some information about the API documentation like title, description, contact, license etc.

For that you can create a bean definition for io.swagger.v3.oas.models.OpenAPI class, which is a root object for representing an OpenAPI document. You can add that bean definition to SpringBoot application class.

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;

@SpringBootApplication
public class UserProjApplication {

	public static void main(String[] args) {
		SpringApplication.run(UserProjApplication.class, args);
	}
	
	@Bean
	public OpenAPI configOpenApi() {
		Contact contact = new Contact();
		contact.setName("TestUser");
		contact.setEmail("tu@tu.com");
		 return new OpenAPI().info(new Info()
				 	.title("User Management System")
				 	.version("1.0")
				 	.description("REST APIs to manage users")
				 	.contact(contact)
				 	.termsOfService("http://swagger.io/terms/")
				 	.license(new License().name("Apache 2.0").url("http://springdoc.org")));
				 
	}
}

With that you can verify that the given information is now getting displayed.

Spring Boot REST API Documentation

Customizing API documentation using Swagger 3 annotations

Using following annotations in your controller class you can enrich the generated documentation.

  1. @ApiResponse- To describe possible API responses.
  2. @Operation- To describe API operations (individual methods in Controller class)
  3. @Parameter- To describe method parameters.
  4. @Tag- To group related API operations.

Using @Tag annotation

You can use @Tag annotation with class or method. It is used to group the related methods. For example, in UserController there are three get methods which can be grouped together using @Tag annotation.

import io.swagger.v3.oas.annotations.tags.Tag;

@RestController
@RequestMapping("/user")
@Tag(name = "User", description = "User management REST APIs")
public class UserController {
  // Fetch all user records
  @GetMapping("/allusers")
  @Tag(name = "get", description = "Get all users")
  public ResponseEntity<List<User>> getAllUsers(){
    return ResponseEntity.ok(userService.getAllUsers());
  }
  
  // Fetch single user
  @GetMapping("/{id}")
  @Tag(name = "get", description = "Get user by ID")
  public ResponseEntity<User> getUserById(@PathVariable("id") int userId){
    //System.out.println("ID to find " + userId);
    User user = userService.getUserById(userId);
      return ResponseEntity.ok(user);
  }
  
  @GetMapping("/type")
  @Tag(name = "get", description = "Get users by user type")
  ResponseEntity<?> getUserByType(@RequestParam("type") String userType){

    System.out.println("userType " + userType);
    List<User> users = userService.getAllUsersByUserType(userType);
    if(users.isEmpty()) {
      return ResponseEntity.noContent().build();
    }
    return ResponseEntity.ok(users);
    
  }
  
  // Delete user record
  @DeleteMapping("/{id}")
  @Tag(name = "delete", description = "Delete user by ID")
  public ResponseEntity<Void> deleteUser(@PathVariable int id){
      userService.deleteUserById(id);
      return ResponseEntity.noContent().build();
      //return new ResponseEntity<String>("User deleted successfully", HttpStatus.OK);   
  }
}

As we have used @Tag annotation at class level that will group all the operations with in the class under "user" tag.

There is a @Tag annotation with name as "get" used with all the three get operations so, the get operations will be grouped together under "get" tag. Same way delete operations will be grouped together under "delete" tag.

Swagger tag Annotation

Using @Operation annotation

Using @Operation annotation you can provide additional information about API operation. For example, adding @Operation annotation on addUser() method (@PostMapping).

// Insert user record
@Operation(summary = "Insert user record", 
    description = "Create a new user, user data is sent as JSON. The response is created user.")
@PostMapping
public ResponseEntity<User> addUser(@Valid @RequestBody User user) {
  User createdUser = userService.addUser(user);
  return ResponseEntity.status(HttpStatus.CREATED).body(createdUser);
}
Swagger Operation Annotation

Using @ApiResponses and @ApiResponse annotations

Using @ApiResponses annotation you can provide information about the possible responses of an operation. Each response in turn is wrapped within ApiResponse annotation. For example, while creating a user, possible responses are 201 (Created), 400 (Bad request- Failed validation), 409(Conflict- User already exists).

// Insert user record
@Operation(summary = "Insert user record", 
    description = "Create a new user, user data is sent as JSON. The response is created user.")
@ApiResponses({
    @ApiResponse(responseCode = "201", content = {@Content(mediaType = "application/json",
            schema = @Schema(implementation = User.class))}),
    @ApiResponse(responseCode = "400", description = "User validation failed",
            content = @Content),
    @ApiResponse(responseCode = "409", description = "Created user already exists",
    content = @Content)})
@PostMapping
public ResponseEntity<User> addUser(@Valid @RequestBody User user) {
  User createdUser = userService.addUser(user);
  return ResponseEntity.status(HttpStatus.CREATED).body(createdUser);
}
Swagger API Responses

Using @Parameter annotation

Using @Parameter annotation you can provide information about the method parameters.

// Fetch single user
@GetMapping("/{id}")
@Tag(name = "get", description = "Get user by ID")
public ResponseEntity<User> getUserById(@Parameter(
        description = "ID of User to be fetched",
        required = true) @PathVariable("id") int userId){
  //System.out.println("ID to find " + userId);
  User user = userService.getUserById(userId);
    return ResponseEntity.ok(user);
}

@GetMapping("/type")
@Tag(name = "get", description = "Get users by user type")
ResponseEntity<?> getUserByType(@Parameter(
        description = "Type of users to be fetched",
        required = true) @RequestParam("type") String userType){

  System.out.println("userType " + userType);
  List<User> users = userService.getAllUsersByUserType(userType);
  if(users.isEmpty()) {
    return ResponseEntity.noContent().build();
  }
  return ResponseEntity.ok(users);
  
}
Swagger Parameter Annotation

That's all for this topic Spring Boot REST API Documentation - OpenAPI, Swagger. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Spring Tutorial Page


Related Topics

  1. Spring Boot Unit Test For REST API
  2. Spring Boot + Spring Security JWT Authentication Example
  3. Spring Boot Spring Initializr
  4. Spring Boot Microservice - Service Registration and Discovery With Eureka
  5. Spring Boot Data JPA Repository Unit Test

You may also like-

  1. Spring depends-on Attribute and @DependsOn With Examples
  2. Spring NamedParameterJdbcTemplate Insert, Update And Delete Example
  3. Spring Transaction Management Example - @Transactional Annotation and JDBC
  4. Spring Batch Processing With List of Objects in batchUpdate() Method
  5. Controlled and Uncontrolled Components in React
  6. How to Remove Elements From an ArrayList in Java
  7. Multi-Catch Statement in Java Exception Handling
  8. Strings in Python With Method Examples

Thursday, September 25, 2025

Named Tuple in Python

In this post we’ll see what are named tuples in Python and how to use them.

In a Python tuple you can store arbitrary elements and access them using index. Now, consider the scenario where tuple is storing a lots of fields, remembering field ordering and accessing them using index becomes quite a task and it is also less readable. In such scenarios named tuple is handy.


Python named tuple

A named tuple is a custom tuple data type which provides ability to refer the items in the tuple by both item names as well as by index position.

A named tuple is similar to plain tuple providing the same performance and the immutability feature.

Like regular tuples, namedtuple instances are also immutable, meaning their values cannot be changed after creation.

Creating named tuple

To create a namedtuple you need to import named tuple from the Collections module.

from collections import namedtuple

Here namedtuple() is a factory function that returns a tuple class.

Syntax for creating named tuple

namedtuple(typename, field_names)

Here typename is the name of the new tuple subclass which is used to create tuple-like objects.

field_names represent fields which are stored in the named tuple. They can be provied as a sequence of strings like ['field1', 'field2'] or as a single string with each fieldname separated by whitespace and/or commas, for example 'field1 field2' or 'field1', 'field2'.

For example-

Employee = namedtuple('Employee', ['id', 'name','age'])

Here named tuple Employee is created which can store field id, name and age.

You can also provide fieldnames as a single string separated by white space.

Employee = namedtuple('Employee', 'id name age')

Python named tuple example

from collections import namedtuple

# Declaring namedtuple()
Employee = namedtuple('Employee', 'id name age')

# Create custom tuple types
e1 = Employee('123', 'Joe', '28')
e2 = Employee('124', 'Lisa', '31')

# Access using index
print('Employee Age', e1[2])

# Access using field name
print('Employee Name', e2.name)

#iterating
for emp in [e1, e2]:
  print('Employee Id', emp.id, 'Employee Name', emp.name, 'Employee Age', emp.age)

Output

Employee Age 28
Employee Name Lisa
Employee Id 123 Employee Name Joe Employee Age 28
Employee Id 124 Employee Name Lisa Employee Age 31

Use cases for named tuple usage

1. You have a list in Python that stores objects of same type. Rather than creating a class, with the fields and then creating objects of that class and storing them in the list, you can create a named tuple which is much easier to create.

Here is the same example as above but uses list to store named tuples.

from collections import namedtuple

# Declaring namedtuple()
Employee = namedtuple('Employee', 'id name age')
#list
employees = []
# storing named tuples
employees.append(Employee('123', 'Joe', '28'))
employees.append(Employee('124', 'Lisa', '31'))

# Access using index
print('Employee Age', employees[0][2])

# Access using field name
print('Employee Name', employees[0].name)

# iterate list
for emp in employees:
    print('Employee Id', emp.id, 'Employee Name', emp.name, 'Employee Age', emp.age)

Output

Employee Age 28
Employee Name Joe
Employee Id 123 Employee Name Joe Employee Age 28
Employee Id 124 Employee Name Lisa Employee Age 31

2. You have a CSV file with a record structure. You can create a similar record structure using named tuple and read records into named tuple directly.

import csv
from collections import namedtuple

# Declaring namedtuple()
Employee = namedtuple('Employee', 'id name age')

for emp in map(Employee._make, csv.reader(open("F:\\NETJS\\employee.csv", "r"))):
    print(emp.id, emp.name, emp.age)

Here note that _make method of the named tuple is used which is a class method that makes a new instance from an existing sequence or iterable.

Methods in named tuple

In addition to the methods inherited from tuples, named tuples support three additional methods.

1. _make(iterable)- Class method that makes a new instance from an existing sequence or iterable. We have already seen an example of this method.

2. _asdict()- Return a new dict which maps field names to their corresponding values.

from collections import namedtuple

# Declaring namedtuple()
Employee = namedtuple('Employee', 'id name age')

# Create custom tuple types
e1 = Employee('123', 'Joe', '28')
print(e1._asdict())

Output

OrderedDict([('id', '123'), ('name', 'Joe'), ('age', '28')])

3. _replace(**kwargs)- Return a new instance of the named tuple replacing specified fields with new values.

from collections import namedtuple

# Declaring namedtuple()
Employee = namedtuple('Employee', 'id name age')

# Create custom tuple types
e1 = Employee('123', 'Joe', 28)
print(e1)
print(e1._replace(age=30))

Output

Employee(id='123', name='Joe', age=28)
Employee(id='123', name='Joe', age=30)

That's all for this topic Named Tuple in Python. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Python Tutorial Page


Related Topics

  1. Concatenating Lists in Python
  2. List Comprehension in Python With Examples
  3. Class And Object in Python
  4. Passing Object of The Class as Parameter in Python
  5. Abstraction in Python

You may also like-

  1. Ternary Operator in Python
  2. Check if String Present in Another String in Python
  3. User-defined Exceptions in Python
  4. Convert String to int in Python
  5. ReentrantLock in Java Concurrency
  6. How HashMap Works Internally in Java
  7. Generics in Java
  8. Array in Java With Examples

Monday, September 22, 2025

Spring Boot Data JPA Repository Unit Test

In this article we'll see how to unit test JPA repositories and the persistence layer of an application in Spring Boot using the specialized annotation @DataJpaTest.

Required dependencies

First of all, ensure spring-boot-starter-test dependency is included. If you are using Maven then this dependency can be included in pom.xml like this.

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-test</artifactId>
  <scope>test</scope>
</dependency>

This starter bundles together common testing libraries like JUnit, Mockito, AssertJ, Spring Boot Test etc.

It is also suggested to use in-memory DB for unit testing the repository layer. This ensures that tests are self-contained and do not rely on an external database. @DataJpaTest annotation automatically configures an embedded database, if it is available on the classpath. So, it is also advisable to use H2 in-memory database, dependency for it is as given below.

<!-- H2 Database -->
<dependency>
  <groupId>com.h2database</groupId>
  <artifactId>h2</artifactId>
  <scope>test</scope>
</dependency>

Benefits of @DataJpaTest annotation

  1. Fast testing- @DataJpaTest annotation disables the full auto-configuration of Spring Boot application. It only configures the components requires for persistence layer testing like Entity classes, data JPA repositories. It also injects a TestEntityManager bean, which provides an alternative to the standard JPA EntityManager that is specifically designed for tests. Other components like Controllers, Service classes are not configured.
  2. Transactional by default- By default, data JPA tests are transactional and roll back at the end of each test which ensures a clean database state for subsequent tests. You can disable this default behavior by using @Transactional annotation. It can be used at the class level.
    @DataJpaTest
    @Transactional(propagation = Propagation.NOT_SUPPORTED) 
    public class UserRepositoryTest(){
    
    }
    

    It can also be used at method level.

  3. Default logging- Logging of executed SQL queries is done by default. This can be controlled using the showSql attribute of the @DataJpaTest annotation.
    @DataJpaTest(showSql = false)
    public class UserRepositoryTest(){
    
    }
    
  4. Providing properties- By using properties attribute you can specify additional Spring Boot configuration properties directly within the @DataJpaTest annotation.
    @DataJpaTest(properties = {
        "spring.datasource.url=jdbc:h2:mem:testdb",
        "spring.jpa.properties.hibernate.show_sql=true"
    })
    public class UserRepositoryTest(){
    }
    
  5. Using real database- @DataJpaTest annotation also provides flexibility to run tests against a real database. You can use the @AutoConfigureTestDatabase annotation for that purpose.
    @DataJpaTest
    @AutoConfigureTestDatabase(replace = Replace.NONE)
    public class UserRepositoryTest(){
    }
    

Unit test for Spring Data JPA Repository using @DataJpaTest annotation

Please refer this post- Spring Boot + Data JPA + MySQL REST API CRUD Example to see the application for which we are going to write unit tests.

Entity class

The JPA entity class used for relational mapping is as given below.

User.java

import java.time.LocalDate;
import java.util.Objects;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotBlank;

@Entity
@Table(name="app_user")
public class User {
  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(name = "ID")
  private int userId;
  
  @NotBlank(message = "First name is required")
  @Column(name="FIRST_NAME")
  private String firstName;
  
  @NotBlank(message = "Last name is required")
  @Column(name="LAST_NAME")
  private String lastName;
  @Column(name="USER_TYPE")
  private String userType;
  @Column(name="START_DATE")
  private LocalDate startDate;
    
  public int getUserId() {
    return userId;
  }
  public void setUserId(int userId) {
    this.userId = userId;
  }
  public String getFirstName() {
    return firstName;
  }
  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }
  public String getLastName() {
    return lastName;
  }
  public void setLastName(String lastName) {
    this.lastName = lastName;
  }
  public String getUserType() {
    return userType;
  }
  public void setUserType(String userType) {
    this.userType = userType;
  }
  public LocalDate getStartDate() {
    return startDate;
  }
  public void setStartDate(LocalDate startDate) {
    this.startDate = startDate;
  }
 // toString(), hashCode() and equals() methods  
}

Please note that giving table name as "User" may cause problem with H2 DB as it is a reserved keyword in H2 database. That is why table name is "app_user".

Data JPA repository

JPA repository interface is as given below.

UserRepository.java

import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import com.netjstech.model.User;

public interface UserRepository extends JpaRepository<User, Integer>{
  List<User> findUserByUserType(String userType);
}

Dialect changes in application.yaml file

If you are using in-memory database H2 for testing then you may add dialect for the same in the application.yaml or application.properties file.

jpa:
    properties:
      hibernate:
        sqldialect: org.hibernate.dialect.H2Dialect

With these changes in the Spring Boot application, you can write tests for the data JPA repository. Here is the full test class with 6 unit tests for save, update, delete, getAll, getById and findUserByUserType().

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;

import java.time.LocalDate;
import java.util.List;
import java.util.Optional;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;

import com.netjstech.dao.UserRepository;
import com.netjstech.model.User;

@DataJpaTest
class UserRepositoryTest {
  
  @Autowired
  UserRepository userRepositroy;
  
  @Test
  void testSaveUser() {
    User user = new User();
    user.setFirstName("Ram");
    user.setLastName("Tiwari");
    user.setUserType("Platinum");
    user.setStartDate(LocalDate.of(2025, 8, 26));
    
    User savedUser = userRepositroy.save(user);
        
    assertThat(savedUser).isNotNull();
    assertNotNull(savedUser.getUserId());
    assertEquals(savedUser.getFirstName(), user.getFirstName());
    
  }
  
  @Test
  void testGetAllUsers() {
    User user1 = new User();
    user1.setFirstName("Ram");
    user1.setLastName("Tiwari");
    user1.setUserType("Platinum");
    user1.setStartDate(LocalDate.of(2025, 8, 26));
    
    User user2 = new User();
    user2.setFirstName("Shyam");
    user2.setLastName("Sharma");
    user2.setUserType("Gold");
    user2.setStartDate(LocalDate.of(2025, 7, 22));
    
    userRepositroy.save(user1);
    userRepositroy.save(user2);
    
    List<User> users = userRepositroy.findAll();
    
    assertNotNull(users);
    assertThat(users.size()).isEqualTo(2);
  }
  
  @Test
  void testGetUserById() {
    User user = new User();
    user.setFirstName("Ram");
    user.setLastName("Tiwari");
    user.setUserType("Platinum");
    user.setStartDate(LocalDate.of(2025, 8, 26));
    User savedUser = userRepositroy.save(user);
    
    Optional<User> userFetched = userRepositroy.findById(savedUser.getUserId());
    
    assertThat(userFetched).isNotEmpty();
    
    assertEquals(user.getLastName(), userFetched.get().getLastName());
  }
  
  @Test
  void testGetAllUsersByType() {
    User user1 = new User();
    user1.setFirstName("Ram");
    user1.setLastName("Tiwari");
    user1.setUserType("Platinum");
    user1.setStartDate(LocalDate.of(2025, 8, 26));
    
    User user2 = new User();
    user2.setFirstName("Shyam");
    user2.setLastName("Sharma");
    user2.setUserType("Gold");
    user2.setStartDate(LocalDate.of(2025, 7, 22));
    
    User user3 = new User();
    user3.setFirstName("Anita");
    user3.setLastName("Verma");
    user3.setUserType("Gold");
    user3.setStartDate(LocalDate.of(2025, 7, 20));
    
    userRepositroy.save(user1);
    userRepositroy.save(user2);
    userRepositroy.save(user3);
    
    String userType = "Gold";
    
    List<User> user = userRepositroy.findUserByUserType(userType);
    
    assertNotNull(user);
    assertEquals(user.size(), 2);
  }
  
  @Test
  void testUpdateUser() {
    User user = new User();
    user.setFirstName("Ram");
    user.setLastName("Tiwari");
    user.setUserType("Platinum");
    user.setStartDate(LocalDate.of(2025, 8, 26));
    
    User savedUser = userRepositroy.save(user);
    
    User userDB = userRepositroy.findById(savedUser.getUserId()).get();
    //update property
    userDB.setUserType("Gold");
    // save again
    savedUser = userRepositroy.save(userDB);
    
    assertNotNull(savedUser);
    assertEquals(savedUser.getUserType(), "Gold");
                
  }
  
  @Test
  void testDeletUser() {
    User user = new User();
    user.setFirstName("Ram");
    user.setLastName("Tiwari");
    user.setUserType("Platinum");
    user.setStartDate(LocalDate.of(2025, 8, 26));
    
    User savedUser = userRepositroy.save(user);
    
    userRepositroy.deleteById(savedUser.getUserId());
    
    Optional<User> userFetched = userRepositroy.findById(savedUser.getUserId());
    
    
    assertThat(userFetched).isEmpty();
  }
}

That's all for this topic Spring Boot Data JPA Repository Unit Test. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Spring Tutorial Page


Related Topics

  1. Spring Boot Unit Test For REST API
  2. Spring Boot REST API Documentation - OpenAPI, Swagger
  3. Spring Boot Microservice - Externalized Configuration With Spring Cloud Config
  4. Spring Boot Microservice + API Gateway + OAuth2 + Keycloak Server
  5. Spring Boot + Data JPA + MySQL REST API CRUD Example

You may also like-

  1. Difference Between @Controller And @RestController Annotations in Spring
  2. Bean Scopes in Spring With Examples
  3. Circular Dependency in Spring Framework
  4. Spring Batch Processing With List of Objects in batchUpdate() Method
  5. Controlled and Uncontrolled Components in React
  6. Greatest Common Divisor (GCD) of Two Numbers Java Program
  7. Multi-Catch Statement in Java Exception Handling
  8. Strings in Python With Method Examples

Greatest Common Divisor (GCD) of Two Numbers Java Program

In this post we'll see how to write a Java program to calculate the greatest common divisor (GCD) of two numbers. It is also known as the highest common factor (HCF).

GCD of two integers is the highest integer that can completely divide both the integers which means remainder is 0 for both of the integers. For example- GCD of 33 and 110 is 11 as that is the highest number which completely divides both numbers.

33 = [1, 3, 11]
110 = [1, 2, 5, 10, 11]

Java program to calculate GCD of two numbers can be written using following approaches-

  1. Writing the logic as per the explanation of calculating GCD. Find the highest number that completely divided both numbers by doing that in a loop.
  2. Using Euclid algorithm (Repeated subtraction logic)
  3. Using Euclid algorithm (Repeated division logic)

1. Finding highest common divisor using loop

In this approach you need to run the loop from 1 up to minimum of two numbers and keep dividing both the numbers with the current loop iteration value. If it completely divides both the numbers then store it. By the end of the loop you will have the GCD.

import java.util.Scanner;
public class GCDCompute {

  public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
    
    System.out.println("Enter first number");
    int a = sc.nextInt();
    
    System.out.println("Enter second number");
    int b = sc.nextInt();

    System.out.println(computeGCD(a, b));      
  }
  
  public static int computeGCD(int a, int b) {
    // edge case where one or both numbers are zero
    if (a == 0) {
        return b;
    }
    if (b == 0) {
        return a;
    }
    int gcd = 1;
    for(int i = 1; i <= a && i <= b; i++) {
      if(a%i == 0 && b%i==0) {
        // completely divides so store the i value
        gcd = i;
      }
    }
    return gcd;
  }
}

Output

Enter first number
112
Enter second number
30
GCD is 2
Enter first number
0
Enter second number
5
GCD is 5

2. Using Euclid algorithm (Repeated subtraction logic) - Java Program

Euclid algorithm is considered more efficient than the general approach shown above. The repeated subtraction variant of Euclid algorithm states that the GCD of two numbers remains same if the greatest of the two numbers is replaced by the difference of the two numbers. This process is repeatedly done until both of the numbers become equal. This number at the end is the GCD of the two given numbers.

For example, if the numbers are 36 and 16.

  1. Greater of the two is 36 so that is replaced by the difference of the two.
  2. So, now the numbers are 20 and 16 again process is repeated
  3. Numbers are now 16 and 4; again subtract 4 from 16 giving 12
  4. Numbers 12 and 4 give 8 and 4
  5. 8 and 4 give 4 and 4, since numbers are equal now so 4 is the GCD.
public class GCDCompute {

  public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
    System.out.println("Enter first number");
    int a = sc.nextInt();
    
    System.out.println("Enter second number");
    int b = sc.nextInt();

    System.out.println("GCD is " + gcdEuclidSubtraction (a, b));
  }
  
  public static int gcdEuclidSubtraction(int a, int b) {
    // edge case where one or both numbers are zero
    if (a == 0) {
        return b;
    }
    if (b == 0) {
        return a;
    }
    while(a != b) {
      if(a > b) {
        a = a - b;
      }else {
        b = b - a;
      }
    }
    
    return a;
  }
}

Output

Enter first number
36
Enter second number
16
GCD is 4

3. Using Euclid algorithm (Repeated division logic) - Java Program

This variant of Euclid algorithm further optimizes it by using division rather than subtraction. It works as follows.

  1. Divide the greater of the two number by the second number.
  2. Assign second number to the first number and remainder to the second number.
  3. Repeat step 1 and 2 until the remainder is zero. The last non-zero remainder is the GCD.

For example, if the two numbers are 36 and 16 then-

  1. Divide 36 by 16 giving remainder as 4.
  2. Now 16 becomes the first number and 4 becomes the second number. Repeat the process of division giving remainder as 0.
  3. That last non-zero remainder 4 is the GCD.
public class GCDCompute {
  public static void main(String[] args) {
    Scanner sc = new Scanner(System.in);
    System.out.println("Enter first number");
    int a = sc.nextInt();
    
    System.out.println("Enter second number");
    int b = sc.nextInt();

    System.out.println("GCD is " + gcdEuclidDivision(a, b));    
  }
  public static int gcdEuclidDivision(int a, int b) {
    int temp;
    // Find the maximum of two numbers, which is assigned to variable a
    if(a < b) {      
      temp = a;
      a = b;
      b = temp;
    }
    while(b != 0) {
      temp = b;
      b = a % b;
      a = temp;
    }
    return a;
  }
}

Output

Enter first number
440
Enter second number
550
GCD is 110
Enter first number
17
Enter second number
37
GCD is 1

That's all for this topic Greatest Common Divisor (GCD) of Two Numbers Java Program. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Java Programs Page


Related Topics

  1. Convert int to String in Java
  2. Convert float to int in Java
  3. Arrange Non-Negative Integers to Form Largest Number - Java Program
  4. How to Convert Date to String in Java
  5. Java String Interview Questions And Answers

You may also like-

  1. Matrix Multiplication Java Program
  2. Reading Delimited File in Java Using Scanner
  3. How to Untar a File in Java
  4. static Block in Java
  5. Is String Thread Safe in Java
  6. Try-With-Resources in Java With Examples
  7. Java ThreadLocal Class With Examples
  8. AtomicLong in Java With Examples

Monday, September 1, 2025

Spring Boot Unit Test For REST API

In this article we'll see how to unit test Spring Boot REST API. As the name "unit test" itself suggests classes should be tested in isolation so in this tutorial we'll write unit tests for Controller class.

Please refer this post- Spring Boot + Data JPA + MySQL REST API CRUD Example to see the Controller class for which we are going to write unit tests.

Annotations and Classes used for writing unit tests in Spring Boot

First of all, ensure spring-boot-starter-test dependency is included. If you are using Maven then this dependency can be included in pom.xml like this.

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-test</artifactId>
  <scope>test</scope>
</dependency>

This starter bundles together common testing libraries like JUnit, Mockito, AssertJ, Spring Boot Test etc.

@WebMvcTest Annotation

It is a specialized annotation that can be used for a Spring MVC test that focuses only on Spring MVC components. Using this annotation disables the full auto-configuration and only enables auto-configuration that is relevant to MVC tests. Similarly, component scanning is limited to beans annotated with:

  • @Controller
  • @ControllerAdvice
  • @JsonComponent

It focuses on testing controllers and other web-related components in isolation, without loading the entire Spring application context.

MockMvc class

MockMvc provides support for testing Spring MVC applications. With MockMvc you don't require to run a web server, MockMvc simulates the full Spring MVC request handling using mock request and response objects.

@MockitoBean annotation

@MockitoBean annotation in Spring Framework is used in test classes to create a mock object. Since we are testing Controller class in isolation so any controller's dependencies on service layer, repository layer is handled using mock instances.

Note that Spring framework leverages the Mockito library to create and manage the mock objects which are used to create stub methods and verify returned values.

Note that @MockitoBean annotation was introduced in Spring Framework 6.2 it replaces @MockBean, which is now deprecated.

For example, if you want to test the UserController class then basic setup using the above mentioned annotations and classes would be as given below-

@WebMvcTest(UserController.class)
public class UserControllerTest {
	@Autowired
	private MockMvc mockmvc;
	@MockitoBean
	private UserService userService;

	@Test
	public void testAddUserReturningBadRequest() throws Exception{
		…
		…
  }
}

Writing unit tests for Spring Boot REST API

Here we want to write unit tests for UserController class which has dependency on UserService and provides methods for adding user (POST), getting all users and getting user by id (GET), updating user (PUT) and delete user by id (DELETE).

If you analyze the code for adding a new user, scenarios you can test are-

  1. If User bean fails validation; which results in MethodArgumentNotValidException being thrown.
  2. If same user is added again; which results in UserAlreadyExistsException being thrown.
  3. All values are correct and the new user is added. Response status sent is 201 (Created) and created user is also returned as response body.

Here is the method that is written in UserController annotated with @PostMapping

// Insert user record
@PostMapping
public ResponseEntity<User> addUser(@Valid @RequestBody User user) {
  User createdUser = userService.addUser(user);
  return ResponseEntity.status(HttpStatus.CREATED).body(createdUser);
}

If you have to write unit tests for the above mentioned scenario 1 and 3 then that is how you can write the tests.

@WebMvcTest(UserController.class)
public class UserControllerTest {
	@Autowired
	private MockMvc mockmvc;
	@Autowired
	private ObjectMapper objectMapper;
	@MockitoBean
	private UserService userService;
	
	@Test
	public void testAddUserReturningBadRequest() throws Exception{
		User user = new User();
		user.setFirstName("");
		user.setLastName("Tiwari");
		user.setUserType("Platinum");
		user.setStartDate(LocalDate.of(2025, 8, 26));
		
		mockmvc.perform(post("/user").contentType(MediaType.APPLICATION_JSON)
				 .content(objectMapper.writeValueAsString(user)))
        		 .andExpect(status().isBadRequest())
        		 .andDo(print());
	}

	@Test
	public void testAddUserReturningCreated() throws Exception{
		User user = new User();
		user.setFirstName("Ram");
		user.setLastName("Tiwari");
		user.setUserType("Platinum");
		user.setStartDate(LocalDate.of(2025, 8, 26));
		// sending Id also
		user.setUserId(1);
		Mockito.when(userService.addUser(any(User.class))).thenReturn(user);
		mockmvc.perform(post("/user").contentType(MediaType.APPLICATION_JSON)
				 .content(objectMapper.writeValueAsString(user)))
        		 .andExpect(status().isCreated())
        		 .andExpect(jsonPath("$.firstName").value("Ram"))
        		 .andExpect(jsonPath("$.userId").value(1))
        		 .andDo(print());        		 		
	}
}

Important points to note here are-

  1. Method testAddUserReturningBadRequest() tests the scenario, if validation fails and method testAddUserReturningCreated() tests the scenario when new user is added.
  2. With Post mapping, you need to send the resource as part of request body in the form of JSON. For that ObjectMapper, provided by Jackson library, is used which converts Java object to and from JSON.
  3. perform method of MockMvc is used to perform a request that uses any Http method (in this case POST), send the type of content and the request body.
  4. You can define expectations by appending one or more andExpect(..) calls after performing a request. These andExpect() methods use MockMvcResultMatchers to assert on the HTTP status, response headers, and response body content. For example andExpect(status().isCreated()), andExpect(jsonPath("$.FIELD_NAME").value("EXPECTED_VALUE"))
  5. andDo(print()) is used to print the details of a MockHttpServletRequest and MockHttpServletResponse to the console during a test execution.
  6. In the method testAddUserReturningCreated(), Mockito.when() is used to specify the return value when the method is called on the mock object. In the above example it specifies; when userService.addUser() is called from the UserController then use the mocked userService instance and return the user object itself.
    Mockito.when(userService.addUser(any(User.class))).thenReturn(user);
    
  7. Notice the use of any() in the userService.addUser() call. It specifies that any user object will do. If you want the exact same user instance to be used then ensure that the User class implements hashCode() and equals() method to enforce object identity. Then you can use Mockito.when() as given below.
    Mockito.when(userService.addUser(user)).thenReturn(user);
    

Same way you can write unit tests for other methods in the UserController class. Here is the complete UserControllerTest class.

UserControllerTest.java

import static org.mockito.ArgumentMatchers.any;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;

import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.web.servlet.MockMvc;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.netjstech.exception.ResourceNotFoundException;
import com.netjstech.model.User;
import com.netjstech.service.UserService;

@WebMvcTest(UserController.class)
public class UserControllerTest {
  @Autowired
  private MockMvc mockmvc;
  @Autowired
  private ObjectMapper objectMapper;
  @MockitoBean
  private UserService userService;
  
//  @Test
//  void test() {
//    fail("Not yet implemented");
//  }
  
  
  @Test
  public void testAddUserReturningBadRequest() throws Exception{
    User user = new User();
    user.setFirstName("");
    user.setLastName("Tiwari");
    user.setUserType("Platinum");
    user.setStartDate(LocalDate.of(2025, 8, 26));
    

    mockmvc.perform(post("/user").contentType(MediaType.APPLICATION_JSON)
         .content(objectMapper.writeValueAsString(user)))
             .andExpect(status().isBadRequest())
             .andDo(print());    
  }

  @Test
  public void testAddUserReturningCreated() throws Exception{
    User user = new User();
    user.setFirstName("Ram");
    user.setLastName("Tiwari");
    user.setUserType("Platinum");
    user.setStartDate(LocalDate.of(2025, 8, 26));
    // sending Id also
    user.setUserId(1);
    Mockito.when(userService.addUser(any(User.class))).thenReturn(user);
    mockmvc.perform(post("/user").contentType(MediaType.APPLICATION_JSON)
         .content(objectMapper.writeValueAsString(user)))
             .andExpect(status().isCreated())
             .andExpect(jsonPath("$.firstName").value("Ram"))
             .andExpect(jsonPath("$.userId").value(1))
             .andDo(print());                 
  }
  
  @Test
  public void testGetAllUsers() throws Exception{
    List<User> userList = new ArrayList<>();
    User user1 = new User();
    user1.setUserId(1);
    user1.setFirstName("Ram");
    user1.setLastName("Tiwari");
    user1.setUserType("Platinum");
    user1.setStartDate(LocalDate.of(2025, 8, 26));
    
    User user2 = new User();
    user2.setUserId(2);
    user2.setFirstName("Shyam");
    user2.setLastName("Sharma");
    user2.setUserType("Gold");
    user2.setStartDate(LocalDate.of(2025, 7, 22));
    userList.add(user1);
    userList.add(user2);
    Mockito.when(userService.getAllUsers()).thenReturn(userList);
    
    mockmvc.perform(get("/user/allusers"))
        .andExpect(status().isOk())
        .andExpect(jsonPath("$[0].firstName").value("Ram"))
        .andExpect(jsonPath("$[1].firstName").value("Shyam"))
        .andDo(print());
  }
  
  @Test
  public void testGetUserReturningResourceNotFound() throws Exception{
    int userId = 1;
    
    Mockito.when(userService.getUserById(userId)).thenThrow(new ResourceNotFoundException("User with ID " + userId + " not found"));
    
    mockmvc.perform(get("/user/"+userId)).andExpect(status().isNotFound())
    .andExpect(content().json("{\"message\": \"Resource Not Found\"}"))
    .andExpect(jsonPath("$.exceptionMessage").value("User with ID " + userId + " not found"))
    .andDo(print());
    
  }
  
  @Test
  public void testGetUserReturningOK() throws Exception{
    int userId = 1;
    User user = new User();
    user.setUserId(1);
    user.setFirstName("Ram");
    user.setLastName("Tiwari");
    user.setUserType("Platinum");
    user.setStartDate(LocalDate.of(2025, 8, 26));
    Mockito.when(userService.getUserById(userId)).thenReturn(user);
    
    mockmvc.perform(get("/user/"+userId)).andExpect(status().isOk())
    .andExpect(jsonPath("$.firstName").value("Ram"))
    .andExpect(jsonPath("$.lastName").value("Tiwari"))
    .andDo(print());
  }
  
  @Test
  public void testUpdateUserReturningResourceNotFound() throws Exception{
    User user = new User();
    user.setUserId(100);
    user.setFirstName("Ram");
    user.setLastName("Tiwari");
    user.setUserType("Platinum");
    user.setStartDate(LocalDate.of(2025, 8, 26));
    
    Mockito.when(userService.updateUser(user))
          .thenThrow(new ResourceNotFoundException("User with ID " + user.getUserId() + " not found"));
    
    mockmvc.perform(put("/user").contentType(MediaType.APPLICATION_JSON)
         .content(objectMapper.writeValueAsString(user)))
         .andExpect(status().isNotFound())
         .andExpect(content().json("{\"message\": \"Resource Not Found\"}"))
         .andExpect(jsonPath("$.exceptionMessage").value("User with ID " + user.getUserId() + " not found"))
         .andDo(print());
    
  }
  
  @Test
  public void testUpdateUserReturningOK() throws Exception{
    User user = new User();
    user.setUserId(1);
    user.setFirstName("Ram");
    user.setLastName("Tiwari");
    user.setUserType("Platinum");
    user.setStartDate(LocalDate.of(2025, 8, 26));
    
    User updatedUser = new User();
    updatedUser.setUserId(1);
    updatedUser.setFirstName("Ram");
    updatedUser.setLastName("Tiwari");
    updatedUser.setUserType("Gold");
    updatedUser.setStartDate(LocalDate.of(2025, 8, 26));
    
    Mockito.when(userService.updateUser(user))
          .thenReturn(updatedUser);
    
    mockmvc.perform(put("/user").contentType(MediaType.APPLICATION_JSON)
         .content(objectMapper.writeValueAsString(user)))
         .andExpect(status().isOk())
         .andExpect(jsonPath("$.userType").value("Gold"))
         .andDo(print());
    
  }
  
  @Test
  public void testDeleteUserReturningResourceNotFound() throws Exception{
    int userId = 100;
    Mockito.doThrow(new ResourceNotFoundException("User with ID " + userId + " not found"))
    .when(userService).deleteUserById(userId);
    
    mockmvc.perform(delete("/user/"+userId))
         .andExpect(status().isNotFound())
         .andExpect(content().json("{\"message\": \"Resource Not Found\"}"))
         .andExpect(jsonPath("$.exceptionMessage").value("User with ID " + userId + " not found"))
         .andDo(print());
    
  }
  
  @Test
  public void testDeleteUserReturningOK() throws Exception{
    int userId = 1;

        Mockito.doNothing().when(userService).deleteUserById(userId);
        //Mockito.verify(userService, Mockito.times(1)).deleteUserById(userId);
        mockmvc.perform(delete("/user/"+userId))
         .andExpect(status().isNoContent())
         .andDo(print());
    
  }

}

Some of the points to note here are-

  1. In the method testGetUserReturningResourceNotFound(), Mockito.when().thenThrow() is used to specify the exception that has to be thrown by the mock object to test user id not found scenario.
  2. Method testGetUserReturningResourceNotFound() shows another way to extract properties from JSON other than using jsonPath.
    andExpect(content().json("{\"message\": \"Resource Not Found\"}"))
    
  3. Method deleteUserById() in the actual UserServiceImpl class doesn't return any value so unit test for that also needs to mock userservice call with that expectation.
    Mockito.doNothing().when(userService).deleteUserById(userId);
    

That's all for this topic Spring Boot Unit Test For REST API. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Spring Tutorial Page


Related Topics

  1. Spring Boot Data JPA Repository Unit Test
  2. Spring Boot + Spring Security JWT Authentication Example
  3. Spring Data JPA - Spring Data Tutorial
  4. Spring Boot Observability - Distributed Tracing, Metrics
  5. Spring Bean Life Cycle

You may also like-

  1. BeanPostProcessor in Spring Framework
  2. Transaction Management in Spring
  3. Spring Web Reactive Framework - Spring WebFlux Tutorial
  4. Spring JdbcTemplate Insert, Update And Delete Example
  5. JVM Run-Time Data Areas - Java Memory Allocation
  6. Comparing Enum to String in Java
  7. Python Lambda Functions With Examples
  8. Angular ngClass Directive With Examples