Saturday, 18 October 2025

 JUnit Testing in Spring Boot – A Complete Guide (Controller, Service, Repository)

Testing is one of the most important parts of building reliable applications. In Spring Boot, we usually test applications at different layers: Controller, Service, and Repository. Each layer has its own testing strategy.

In this blog, we’ll build a simple example step by step and write JUnit tests for each layer.


๐Ÿ“‚ Example Project – User Management

We will use a simple User entity with the following layers:

  • Controller → Handles HTTP requests

  • Service → Contains business logic

  • Repository → Interacts with the database


๐ŸŸข 1. Entity & DTO

// User.java (Entity/DTO) public class User { private String id; private String name; // Constructors public User(String id, String name) { this.id = id; this.name = name; } // Getters & Setters public String getId() { return id; } public String getName() { return name; } }

๐ŸŸข 2. Repository

// UserRepository.java import org.springframework.data.jpa.repository.JpaRepository; public interface UserRepository extends JpaRepository<User, String> { }

๐ŸŸข 3. Service

// UserService.java import org.springframework.stereotype.Service; import java.util.Optional; @Service public class UserService { private final UserRepository userRepository; public UserService(UserRepository userRepository) { this.userRepository = userRepository; } public User getUser(String id) { return userRepository.findById(id).orElse(null); } public User saveUser(User user) { return userRepository.save(user); } }

๐ŸŸข 4. Controller

// UserController.java import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/users") public class UserController { private final UserService userService; public UserController(UserService userService) { this.userService = userService; } @GetMapping("/{id}") public User getUser(@PathVariable String id) { return userService.getUser(id); } @PostMapping public User createUser(@RequestBody User user) { return userService.saveUser(user); } }

๐Ÿงช Writing Tests

Now let’s write JUnit tests for each layer.


๐Ÿ”น 1. Service Layer Test

Here we test the business logic in UserService.
We don’t want to hit the database → so we mock the repository using @Mock and inject it into the service with @InjectMocks.

// UserServiceTest.java import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class UserServiceTest { @Mock private UserRepository userRepository; @InjectMocks private UserService userService; @Test void testGetUser() { // Arrange User mockUser = new User("101", "Aamir"); when(userRepository.findById("101")).thenReturn(Optional.of(mockUser)); // Act User result = userService.getUser("101"); // Assert assertThat(result.getName()).isEqualTo("Aamir"); } @Test void testSaveUser() { User user = new User("102", "John"); when(userRepository.save(user)).thenReturn(user); User saved = userService.saveUser(user); assertThat(saved.getId()).isEqualTo("102"); } }

๐Ÿ”น 2. Controller Layer Test

Here we want to test the HTTP endpoints without starting the full application.
We use:

  • @WebMvcTest(UserController.class) → Loads only Controller + MVC beans

  • MockMvc → To simulate HTTP requests

  • @MockBean → To mock the service layer inside Spring Context

// UserControllerTest.java import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import static org.mockito.Mockito.when; 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.result.MockMvcResultMatchers.*; @WebMvcTest(UserController.class) class UserControllerTest { @Autowired private MockMvc mockMvc; @MockBean private UserService userService; @Test void testGetUser() throws Exception { User mockUser = new User("101", "Aamir"); when(userService.getUser("101")).thenReturn(mockUser); mockMvc.perform(get("/users/101") .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(jsonPath("$.name").value("Aamir")); } @Test void testCreateUser() throws Exception { User user = new User("102", "John"); when(userService.saveUser(Mockito.any(User.class))).thenReturn(user); mockMvc.perform(post("/users") .contentType(MediaType.APPLICATION_JSON) .content("{\"id\":\"102\",\"name\":\"John\"}")) .andExpect(status().isOk()) .andExpect(jsonPath("$.id").value("102")) .andExpect(jsonPath("$.name").value("John")); } }

๐Ÿ”น 3. Repository Layer Test

Here we want to test the database interaction.
We use:

  • @DataJpaTest → Loads only JPA-related beans with an in-memory H2 database

  • Tests run against a temporary DB (no need to start the full application)

// UserRepositoryTest.java import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; @DataJpaTest class UserRepositoryTest { @Autowired private UserRepository userRepository; @Test void testSaveAndFindUser() { User user = new User("201", "Alice"); userRepository.save(user); Optional<User> found = userRepository.findById("201"); assertThat(found).isPresent(); assertThat(found.get().getName()).isEqualTo("Alice"); } }

๐Ÿ“Š Summary

LayerAnnotations UsedPurpose
Service@ExtendWith(MockitoExtension.class), @Mock, @InjectMocksTest business logic with mocked dependencies
Controller@WebMvcTest, @MockBean, MockMvcTest REST endpoints without starting server
Repository@DataJpaTestTest JPA queries with in-memory DB

✅ Conclusion

  • Service Layer → Focus on business logic, mock dependencies.

  • Controller Layer → Focus on API endpoints, mock the service.

  • Repository Layer → Focus on DB interaction using in-memory DB.

By following this layered approach, you ensure that each part of your Spring Boot application is properly tested in isolation.

Deep Dive into JUnit + Mockito in Spring Boot

In Spring Boot, we usually use JUnit 5 along with Mockito to write unit and integration tests.
Below are some of the most important annotations and tools you’ll come across while testing your application.


๐Ÿงช @Mock

  • This annotation creates a mock object (a dummy object) for a dependency.

  • It does not run any real logic — instead, you define what it should return when a method is called.

๐Ÿ‘‰ Example:

@Mock private UserRepository userRepository;

Here, the real database calls are not made. We can use Mockito’s when(...).thenReturn(...) to specify what this mock should return.


๐Ÿงฐ @InjectMocks

  • This creates a real instance of the class under test and automatically injects the mock dependencies (created with @Mock) into it.

  • This is mainly used when you are testing the Service layer or any class that has dependencies.

๐Ÿ‘‰ Example:

@ExtendWith(MockitoExtension.class) class UserServiceTest { @Mock private UserRepository userRepository; @InjectMocks private UserService userService; // <-- mock repository will be injected here @Test void testFindUser() { when(userRepository.findById("101")) .thenReturn(Optional.of(new User("101", "Aamir"))); User user = userService.getUser("101"); assertThat(user.getName()).isEqualTo("Aamir"); } }

๐Ÿ“Œ Why use @InjectMocks?
Without it, you would have to manually create the object like:

UserService userService = new UserService(userRepository);

@InjectMocks makes this automatic and cleaner.


๐ŸŒฟ @MockBean

  • This is used in Spring Test Context (e.g., with @WebMvcTest or @SpringBootTest).

  • It registers a mock bean in the Spring ApplicationContext, replacing the actual bean.

๐Ÿ‘‰ Example:

@WebMvcTest(UserController.class) class UserControllerTest { @Autowired private MockMvc mockMvc; @MockBean private UserService userService; // replaces the real service in the Spring context @Test void testGetUser() throws Exception { when(userService.getUser("123")).thenReturn(new User("123", "Aamir")); mockMvc.perform(get("/users/123")) .andExpect(status().isOk()) .andExpect(jsonPath("$.name").value("Aamir")); } }

๐Ÿ“Œ @MockBean vs @Mock:

  • @Mock → creates a Mockito mock object only (no Spring involved).

  • @MockBean → creates a mock and replaces the actual Spring bean with it in the ApplicationContext.


๐Ÿ•ธ️ @WebMvcTest

  • This annotation loads only the Spring MVC components required for controller testing.

  • It does not load the entire application context (like services, repositories, etc.).

  • This is used to test the Controller layer in isolation.

๐Ÿ‘‰ Example:

@WebMvcTest(UserController.class) class UserControllerTest { ... }

๐Ÿงญ MockMvc

  • MockMvc allows you to simulate HTTP requests (GET, POST, etc.) without actually starting the server.

  • You can test endpoints, status codes, and responses just like calling APIs from a browser or Postman.

๐Ÿ‘‰ Example:

mockMvc.perform(get("/users/1") .accept(MediaType.APPLICATION_JSON)) .andExpect(status().isOk()) .andExpect(jsonPath("$.name").value("Aamir"));

๐Ÿ“ Summary Table

AnnotationPurposeScope
@MockCreates a Mockito mock (no Spring involved)Plain Unit Tests
@InjectMocksCreates real instance and injects mocks into itPlain Unit Tests
@MockBeanRegisters mock in Spring ApplicationContext, replacing real beanSpring Context Tests
@WebMvcTestLoads only controller-related beans for testingController Layer Testing
MockMvcSimulates HTTP requests without running the serverController Layer Testing

✅ How to Choose

  • Service Layer Testing → Use @Mock + @InjectMocks

  • Controller Layer Testing → Use @WebMvcTest + @MockBean + MockMvc

  • Repository Layer Testing → Use @DataJpaTest (integration with in-memory DB)


This clear separation helps keep tests fast, maintainable, and focused on each layer.

Wednesday, 15 October 2025

Spring Boot

 Spring Boot


Sunday, 24 August 2025

Frontend Interview Preparation - Q&A



Q1. How can you define a selector that only works for a screen for more than 700px?

You can use **CSS media queries**:

css
@media screen and (min-width: 700px) {
  selector {
    /* styles */
  }
}


This ensures styles only apply when the screen width is more than 700px. Useful for responsive design.

Q2. What is difference between let, const and var

-In JavaScript, let, const, and var are all used to declare variables, but they differ in scope, reassignment rules, and hoisting behavior

1. var

  • Scope: Function-scoped (or global if declared outside a function) — not block-scoped.
  • Reassignment: Can be reassigned and redeclared.
  • Hoisting: Hoisted to the top of the scope, initialized with undefined.
  • Use today: Rarely used now because it can cause bugs due to lack of block scope.

function testVar() {
    if (true) {
        var x = 10;
    }
    console.log(x); // 10 ✅ (var ignores block scope)
}


2. let

  • Scope: Block-scoped ({}), meaning it’s only accessible inside the block where it’s declared.
  • Reassignment: Can be reassigned but not redeclared in the same scope.
  • Hoisting: Hoisted but not initialized — accessing it before declaration causes a ReferenceError (Temporal Dead Zone).
  • Use: For variables whose values may change.

function testLet() { if (true) { let y = 20; } console.log(y); // ❌ ReferenceError }

3. const

  • Scope: Block-scoped.
  • Reassignment: Cannot be reassigned.
  • Hoisting: Same as let — hoisted but not initialized (Temporal Dead Zone).
  • Use: For variables whose values should not change (constants).
  • Note: For objects/arrays, the reference can’t change, but the contents can be modified.

const z = [1, 2, 3]; z.push(4); // ✅ Allowed (array contents can change) z = [5, 6]; // ❌ TypeError (reference change not allowed)


Q. What is hoisting?

Hoisting in JavaScript means that variable and function declarations are moved to the top of their scope (global or function) during the compilation phase, before the code is executed.

But there’s a catch — only declarations are hoisted, not initializations.

1️⃣ Hoisting with var

  • var is hoisted and initialized with undefined.
  • That’s why you can use a var variable before its declaration without getting an error (though it’s bad practice).

console.log(a); // undefined ✅ var a = 10; // Declaration is hoisted, initialization happens later

2️⃣ Hoisting with let and const

  • These are hoisted but not initialized.
  • They stay in the Temporal Dead Zone (TDZ) from the start of the scope until their declaration line.
  • Accessing them before declaration throws a ReferenceError.

console.log(b); // ❌ ReferenceError let b = 20;

3️⃣ Hoisting with Functions

  • Function declarations are fully hoisted — both their name and body.
  • You can call them before their definition.

sayHello(); // ✅ Works function sayHello() { console.log("Hello!"); }
  • Function expressions (assigned to var, let, const) behave like variables — only the variable declaration is hoisted, not the function value.

sayHi(); // ❌ TypeError: sayHi is not a function var sayHi = function() { console.log("Hi!"); };


Q3. How could you add an element to the end and beginning of the array?

- Add at end: `arr.push(element)`
- Add at beginning: `arr.unshift(element)`

Example:

let arr = [2,3];
arr.push(4); // [2,3,4]
arr.unshift(1); // [1,2,3,4]

Q4. How to validate a type of a variable in JS?

Use `typeof` operator:

typeof 123; // 'number'
typeof 'abc'; // 'string'
typeof true; // 'boolean'


Q5. Few examples of typeof in TypeScript?

TypeScript enhances `typeof` for type queries.

let msg = "Hello";
let len: typeof msg; // string

function fn(x: number) { return x; }
let y: ReturnType<typeof fn>; // number

Q6. How to define a parameter as an Option?

In TypeScript, mark it as optional using `?`:

function greet(name?: string) {
  console.log(name ? `Hello, ${name}` : "Hello");
}


Q7. How to define the constant inside of a class?

Use `static readonly`:

class MyClass {
  static readonly PI = 3.14;
}
console.log(MyClass.PI);


Q8. Few examples of main building blocks in Angular

- **Modules**: Organize code.
- **Components**: UI building blocks.
- **Templates**: HTML with Angular syntax.
- **Services**: Business logic.
- **Directives**: Modify DOM behavior.
- **Dependency Injection**: Provides services.

Q9. What is the difference between @Input and @Output, and where should they be used?

- @Input: Pass data **from parent to child**.
- @Output: Send events/data **from child to parent**.

Example:

// child.component.ts
@Input() title: string;
@Output() clicked = new EventEmitter<string>();

Q10. Promises Vs Observables

- **Promise**: Single value, eager, no cancellation.
- **Observable**: Multiple values over time, lazy, supports cancellation.

Example: Use Promise for one-time HTTP call, Observable for streaming data (like WebSockets).

Q11. Whats @ViewChild

Decorator to access child components, directives, or DOM elements directly.

@ViewChild('inputRef') input: ElementRef;
ngAfterViewInit() { this.input.nativeElement.focus(); }

Q12. What is HostListener

@HostListener listens to events on the host element.


@Directive({selector: '[hoverHighlight]'})
class HoverDirective {
  @HostListener('mouseenter') onEnter() { ... }
}

Q13. Subject Vs BehaviourSubject

- **Subject**: Emits values only to subscribers after subscription.
- **BehaviorSubject**: Stores last value and immediately sends it to new subscribers.

Use BehaviorSubject when you want subscribers to always have the latest state.

Q14. JS synchronous or async?

JS is **single-threaded synchronous** but uses **event loop** for async operations.
- Synchronous: Code runs line by line.
- Asynchronous: `setTimeout`, Promises handled via event loop.

Q15. Lifecycle hooks in Angular

- ngOnInit: Init logic.
- ngOnChanges: When @Input changes.
- ngAfterViewInit: After view init.
- ngOnDestroy: Cleanup.


Helps manage component lifecycle.

Q16. console.log(1=='1') and console.log(1==='1')

- '==': true (because it coerces types).
- '===`': false (strict comparison).

Q17. So (4 == '5') will be false?

Yes, because type coercion converts `'5'` to number, and `4 != 5`. So result is **false**.

Q18. Anonymous functions

Functions without a name. Often used in callbacks.

setTimeout(function(){ console.log('Hi'); }, 1000);

Q19. Calling Nested function func()()

If a function returns another function, you can call it immediately:

function outer() {
  return function inner() { console.log('Hi'); }
}
outer()(); // Hi

Q20. Engine Execution: setTimeout and console.log

`console.log` executes first (synchronous).
`setTimeout` goes to Web API, then callback queue, then event loop executes it **after main stack is empty**.

Q21. Global Context in JS

The default execution context: In browsers it's the **window object**, in Node.js it's **global**.

Q22. GET and POST Security aspects

- **GET**: Params visible in URL (less secure).
- **POST**: Params in body (more secure).

Never send password in GET; risk of logging, bookmarking, caching.

Q23. Real time example where you can use ViewChilds

Example: Auto-focusing an input field after loading a component.

@ViewChild('searchBox') search: ElementRef;
ngAfterViewInit(){ this.search.nativeElement.focus(); }


Q24. Parent/Child Component with @Input and @Output

Parent passes data to child via `@Input`, child emits events to parent via `@Output`.


// parent.html
<child [title]="parentTitle" (notify)="onNotify($event)"></child>


This enables component communication.

Q26. How to expose REST web services to frontend in Spring

๐Ÿ”น 1. Create a REST Controller in Spring Boot

In Spring, you expose REST endpoints using @RestController and @RequestMapping (or @GetMapping, @PostMapping, etc.).

import org.springframework.web.bind.annotation.*;
import java.util.*;

@RestController
@RequestMapping("/api/users")

public class UserController {

    // Example GET API
    @GetMapping
    public List<String> getAllUsers() {
        return Arrays.asList("Aamir", "John", "Maria");
    }

    // Example POST API
   @PostMapping
    public String createUser(@RequestBody String userName) {
        return "User " + userName + " created successfully!";
    }
}

๐Ÿ‘‰ This exposes endpoints:

  • GET http://localhost:8080/api/users
  • POST http://localhost:8080/api/users

๐Ÿ”น 2. CORS (Cross-Origin Resource Sharing)

Frontend (Angular/React) usually runs on a different port (e.g., Angular: http://localhost:4200).
To allow frontend calls, enable CORS:

Option A: At Controller Level

@RestController
@RequestMapping("/api/users")
@CrossOrigin(origins = "http://localhost:4200")  // Allow Angular app

public class UserController {
    // ...
}

Option B: Global Configuration

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

 @Configuration
public class WebConfig {

    
@Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurer() {

            
@Override     
            
public void addCorsMappings(CorsRegistry registry) {
           registry.addMapping("/api/**")
                        .allowedOrigins("http://localhost:4200")
                        .allowedMethods("GET", "POST", "PUT", "DELETE");
            }
        };
    }
}


๐Ÿ”น 3. Consume API from Frontend

Angular Example (using HttpClient)

// user.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

 @Injectable({
 providedIn: 'root'
})

export class UserService {
  private apiUrl = 'http://localhost:8080/api/users';

  constructor(private http: HttpClient) {}

  getUsers(): Observable<string[]> {
   return this.http.get<string[]>(this.apiUrl);
  }

   addUser(userName: string): Observable<string> {
   return this.http.post<string>(this.apiUrl, userName);
  }
}


๐Ÿ”น 4. Security Aspect

  • If using Spring Security, you need to configure allowed endpoints for frontend.
  • Example (permit /api/** without auth):

http.csrf().disable()
  .authorizeRequests()
    .antMatchers("/api/**").permitAll()
    .anyRequest().authenticated();


 Summary (Interview Answer):
To expose REST web services to frontend in Spring Boot:

  1. Create endpoints using @RestController.
  2. Handle cross-origin requests with @CrossOrigin or WebMvcConfigurer.
  3. Frontend calls these endpoints using HTTP clients (e.g., Angular HttpClient, React fetch).
  4. If Spring Security is enabled, configure access rules for APIs.

Q27. URL Shortening using TinyURL

You generate a unique key for a long URL and store mapping in DB. When user hits tiny URL, backend redirects to original.

Can use hashing + base62 encoding.

Q28. AuthGuard & Interceptor in Angular

- **AuthGuard**: Protects routes (checks if user logged in).
- **Interceptor**: Intercepts HTTP requests, adds JWT, handles errors.

Together they ensure security in Angular apps.