Lessons available in both languages
Java Backend · Interview Prep

Spring Testing interview questions & answers

203+ real Spring Testing interviewquestions with model answers, plus free lessons to learn the concepts. Prepare in English & Hinglish, then practise with an AI mock interview.

18 topics · 203+ questions

Hirenix kaise padhata hai

Ek chapter. 90 minute.
Interview ke liye taiyaar.

Har concept ek real-world problem se — jaisa production code mein aata hai, waisa. Ratna nahi padta, samajh aa jaata hai. Har question ka model answer diya hai: interviewer ko exactly kya bolna hai, aur kyun. Phir usi chapter ka AI mock interview.

  • 📖Concept, 5 min meinJargon nahi — seedhi baat
  • 🛠️Real-world problemJaisa production code mein aata hai
  • 💬Model answerInterview mein kya bolna hai
  • 🧠FlashcardsRevision 10 min mein
  • 🤖AI mock interviewFollow-up bhi poochta hai
  • 📊Weak topicsKahan phans rahe ho, pata chale
Ye chapter shuru karo — free🌐 English🇮🇳 Hinglish
A student learning an interview concept on Hirenix at home
Video playlistsyllabus ke hisaab se18h+
Hirenix chapterinterview ke hisaab se90 min

Farq content ka nahi, filter ka hai — sirf wahi jo production mein actually use hota hai aur interview mein actually poocha jaata hai. Kitaabi topics jo industry mein kahin nahi chalte, wo yahan nahi milenge.

Certificates

Learn all 18 topics and earn your Spring Testing certificate

Finish every Spring Testing topic and a certificate with your name on it is issued instantly. No exam, no waiting. Download it, share it on LinkedIn, and put it on your resume.

  • Chapter certificate

    Your name, the Spring Testing course and all 18 topics on it.

  • Stack Mastery certificate

    Finish every Java Backend chapter for the Java Backend Stack Mastery certificate.

  • Anyone can verify it

    Each certificate has a QR code and a public link at hirenix.in/verify.

Start this chapter — freeCertificates are included with Pro.
Sample Hirenix Certificate of Completion with the learner's name and a verification QR code

Verified credential

hirenix.in/verify/…

Lessons available in both languages

What you’ll learn

  • ●Spring apps ko test kyun karein: unit, slice aur full-context tests
  • ●JUnit 5 ki buniyaad: lifecycle, assertions aur parameterized tests
  • AssertJ aur aise assertions jo apni failure khud samjhayeinPro
  • Mockito ki buniyaad: mock, stub, verify aur strict stubsPro
  • SpringBootTest aur uske web environments: MOCK vs RANDOM_PORTPro
  • Test slices: WebMvcTest kya load karta hai aur kya chhod deta haiPro
  • MockMvc: request banana aur response ko assert karnaPro
  • MockitoBean, deprecated MockBean, aur context me bean badalnaPro
  • DataJpaTest: embedded database aur apne aap rollbackPro
  • Spring Security ko test karna: WithMockUser, csrf aur denial testsPro
  • Context caching: ek test suite tez aur doosri dheemi kyun hoti haiPro
  • TestConfiguration, test properties aur profilesPro
  • Testcontainers aur asli databases: jab H2 kaafi nahi hotaPro
  • Transactional test ke jaal: galat wajah se pass hone wale testsPro
  • RecapPro
  • ●Project: Secure Job Board API ke tests likho
  • Project: DataJpaTest se Repository TestsPro
  • Project: Dheemi Test Suite ko Tez KaroPro

Free lessons

Spring apps ko test kyun karein: unit, slice aur full-context tests

Jo Spring test suites dheemi ya naazuk lagti hain, unki jad aksar ek hi hoti hai: har test poori application start karta hai, kyunki @SpringBootTest pehla annotation tha jo kisi ko mila. Ye topic is baare me hai ki sabse chhota test kaunsa hai jo sach me wo bug pakad sake jiski aapko chinta hai - aur ye sabit karna ki test fail bhi ho sakta hai.

Ek application, test ke teen level

Demo job board ka ek chhota hissa hai. ApplicationScorer me sirf logic hai (experience paanch par ruk jaata hai, Java aane par paanch aur judte hain), aur ScoreController use HTTP par deta hai. Wahi code chaar tarah test hota hai:

Level Kya banta hai Is run me naapa gaya
Unit - plain JUnit sirf new ApplicationScorer() pass; class me kahin Spring annotation nahi
Unit - Mockito controller aur MockitoExtension se ek @Mock scorer pass; ab bhi koi Spring annotation nahi, koi context nahi
Slice - @WebMvcTest sirf web layer; scorer ki jagah @MockitoBean 168 bean definitions
Full - @SpringBootTest poora application context 279 bean definitions

Ye gintiyan Spring Boot 3.5.16 par isi ek application ki hain, isi exact classpath par - inhe har jagah ka number mat bataiye. Ye is app ke liye bhi tay nahi hain: environment me Bean Validation (hibernate-validator) judne se pehle wahi do tests 165 aur 276 ginte the. Ek library, dono me teen definitions zyada. Shakal bataiye: slice ne poore context ka lagbhag 60% register kiya, us app me jiski apni sirf do classes hain. Asli service me repositories, HTTP clients aur schedulers hote hain, aur test ka samay isi farak me jaata hai. (Is environment me timing naapi nahi gayi, isliye ye topic speed ka koi dawa nahi karta.)

Har level kya pakad sakta hai aur kya nahi

  • Plain unit test niyam sabit karta hai: score(12, true) 10 hai kyunki experience ruk jaata hai. Wo nahi bata sakta ki controller /score par map hai, ya Spring bean bana bhi pa raha hai.
  • Mockito unit test sabit karta hai ki controller sahi tarah kaam aage deta hai. @ExtendWith(MockitoExtension.class) bina kisi container ke @Mock aur @InjectMocks deta hai - ye ab bhi unit test hai.
  • Slice web wiring sabit karta hai: request mapping, JSON, security chain, @ControllerAdvice. Controller ke neeche ki har cheez ko wo jaan-boojh kar mock se badal deta hai.
  • Full-context test ek integration test hai: asli beans aapas me jude hue. Teeno me se sirf yahi level fail hota hai jab koi bean ban hi na sake.
  • End-to-end test chalti hui system ko bahar se chalata hai - browser ya asli database ke saath asli HTTP. Is environment me browser nahi hai, isliye yahan ye sirf ek paribhasha hai.

Yahi test pyramid hai: neeche bahut saare unit tests, beech me kam slices aur integration tests, upar bahut kam end-to-end tests.

spring-boot-starter-test classpath par kya laata hai

spring-boot-starter-test 3.5.16 ka POM seedha padh kar (artifact padha gaya, chalaya nahi): spring-boot-test, spring-boot-test-autoconfigure, spring-test, JUnit Jupiter, mockito-core, mockito-junit-jupiter, AssertJ, Hamcrest, JsonPath, JSONassert, awaitility aur xmlunit-core. Ek test dependency, aur is chapter ka har auzaar maujood.

Jo test aapne kabhi fail hote nahi dekha, wo bahut kam sabit karta hai

Run 2 wahi assertion rakhta hai aur sirf expected value badalta hai, 10 se 11. Runner jawab deta hai expected: <11> but was: <10> aur [ 1 tests failed ]. Ise negative control kehte hain: kisi green test par bharosa karne se pehle use ek baar jaan-boojh kar todiye aur laal hote dekhiye. Is chapter me aage baar-baar aise tests milenge jo galat wajah se green hain, aur ye aadat unse sabse sasta bachav hai.

Kaunsa level kab use karein: sabse neeche wale us level se shuru kijiye jo bug dekh sake. Business rule -> plain unit test. Collaborators ko bulane wali class -> Mockito unit test. Request mapping, JSON ki shakal, validation ya security rules -> @WebMvcTest. Bean wiring, configuration, kai layers ek saath -> @SpringBootTest.

Kab NAHI @SpringBootTest lena chahiye: jab sawaal ek class ke logic ka ho. Aap poore context ki keemat chukate hain (yahan 279 definitions, jabki zaroorat zero ki thi) aur failure aapke tode hue niyam ki jagah application startup ki taraf ishara karta hai.

Trade-off: ye dono taraf kaatta hai. Sirf mocks se bani suite poori green ho sakti hai jabki asli application start hi na ho, kyunki mocks wiring kabhi jaanchte nahi. Isi kaam ke liye kuch full-context integration tests zaroor rakhiye.

// ---- t1/ApplicationScorer.java : pure logic, no database, no HTTP ----
@Service
public class ApplicationScorer {
  public int score(int yearsExperience, boolean knowsJava) {
    int s = Math.min(yearsExperience, 5);          // experience counts up to 5
    return knowsJava ? s + 5 : s;
  }
}

// ---- t1/ScoreController.java ----
@RestController
public class ScoreController {
  private final ApplicationScorer scorer;
  public ScoreController(ApplicationScorer scorer) { this.scorer = scorer; }
  @GetMapping("/score") public int score(@RequestParam int years, @RequestParam boolean java) { return scorer.score(years, java); }
}

// ---- Level 1: plain JUnit. No Spring, no context, just `new`. ----
class ScorerUnitTest {
  @Test void capsExperienceAtFive() {
    ApplicationScorer scorer = new ApplicationScorer();
    assertEquals(10, scorer.score(12, true));
    System.out.println("UNIT  score(12,true)=" + scorer.score(12, true) + "  (no ApplicationContext created)");
  }
}

// ---- Still level 1: a controller tested with Mockito alone. No Spring context is started. ----
@ExtendWith(MockitoExtension.class)
class ScoreControllerMockitoTest {
  @Mock ApplicationScorer scorer;
  @InjectMocks ScoreController controller;
  @Test void delegatesToScorer() {
    given(scorer.score(3, true)).willReturn(8);
    assertEquals(8, controller.score(3, true));
    verify(scorer).score(3, true);
    System.out.println("MOCKITO controller.score(3,true)=" + controller.score(3, true) + "  (no ApplicationContext created)");
  }
}

// ---- Level 2: a slice. Only the web layer is built; the scorer is replaced by a mock. ----
@WebMvcTest(ScoreController.class)
class ScorerSliceTest {
  @Autowired ApplicationContext ctx;
  @MockitoBean ApplicationScorer scorer;
  @Test void webLayerOnly() {
    System.out.println("SLICE beans=" + ctx.getBeanDefinitionCount()
        + "  controller=" + ctx.getBeanNamesForType(ScoreController.class).length);
  }
}

// ---- Level 3: the whole application context, real beans wired together. ----
@SpringBootTest
class ScorerFullTest {
  @Autowired ApplicationContext ctx;
  @Autowired ApplicationScorer scorer;
  @Test void wholeContext() {
    assertEquals(10, scorer.score(12, true));
    System.out.println("FULL  beans=" + ctx.getBeanDefinitionCount() + "  scorer=" + scorer.getClass().getSimpleName());
  }
}

// ---- t1neg: same assertion with a deliberately WRONG expectation - proves the runner really fails. ----
class ScorerNegativeControlTest {
  @Test void wrongExpectation() { assertEquals(11, new ApplicationScorer().score(12, true)); }
}

// Run with the JUnit Platform console launcher (Boot 3.5.16, JUnit 5.12.2, Mockito 5.17.0, JDK 17):
//   run 1: --select-class ScorerUnitTest ScoreControllerMockitoTest ScorerSliceTest ScorerFullTest
//   run 2: --select-class ScorerNegativeControlTest

JUnit 5 ki buniyaad: lifecycle, assertions aur parameterized tests

JUnit 5 ek akeli library nahi hai. Ye ek platform aur uske engines hain, aur pehle hafte ki lagbhag har bharmane wali baat - tests ka ajeeb kram me chalna, set kiya hua field achanak khaali milna, setup method jise JUnit chalane se mana kar de - isi dhaanche aur lifecycle se samajh aati hai. Neeche ki har cheez chalayi gayi hai, rati nahi gayi.

Platform, Jupiter, Vintage

Launcher ka chhapa hua tree dekhiye. Uski teen jadein hain: JUnit Platform Suite, JUnit Jupiter aur JUnit Vintage.

  • JUnit Platform tests dhoondhta hai, chalata hai aur nateeje batata hai. Build tools aur IDE tests isi ke zariye chalate hain (JUnit documentation ke mutabik - yahan koi build tool chalaya nahi gaya). Is chapter ke environment me koi build tool hai hi nahi - wo Platform ka console launcher seedha chalata hai, isiliye chapter ka har output [ 1 tests failed ] jaisi lines par khatam hota hai.
  • Jupiter JUnit 5 ke programming model ka engine hai: @Test, @BeforeEach, @ParameterizedTest, @Nested, @ExtendWith.
  • Vintage wo engine hai jo purane JUnit 4 tests usi Platform par chalata hai. Ye yahan isliye dikha kyunki standalone launcher use saath laata hai; use chalane ko kuch mila nahi.

Lifecycle, naapa hua

LifecycleTest ke output me teen baatein dikhti hain:

  1. @BeforeAll sabse pehle ek baar chala, aur @AfterAll bilkul aakhir me ek baar. Dono static hain.
  2. Har test method ke liye test class ka naya instance bana - do methods, do alag instance ids (210ab13f, 3b35a229). Ek test jo field set karta hai, wo agle test me hota hi nahi. Yahi default tests ko instance fields ke zariye state leak karne se rokta hai.
  3. @BeforeEach aur @AfterEach ne har test ko lapeta, usi test ke apne instance par.

@BeforeAll static kyun hona chahiye? Kyunki wo kisi bhi instance ke banne se pehle chalta hai. static hataiye aur JUnit class chalane se hi mana kar deta hai:

@BeforeAll method 'void t2bad.NonStaticBeforeAllTest.setUp()' must be static unless the test class is annotated with @TestInstance(Lifecycle.PER_CLASS).

Us class ke summary par dhyaan dijiye: 0 tests successful, 0 tests failed. Class container ki tarah fail hui, isliye uska test chala hi nahi - jis report me koi failure na dikhe, wo us report jaisi nahi hai jisme pass dikhein.

Kram wo nahi jo aapne likha

first pehle declare hua hai aur second baad me, par second pehle chala. Nested class me bhi spaces are rejected, null is rejected se pehle chala - phir se source ka ulta. JUnit 5 declaration ke kram ka waada nahi karta. Jo test sirf kisi aur test ke chalne ke baad pass ho, wo toota hua hai, aur @TestMethodOrder us kam hi aane wale case ke liye hai jahan kram khud test ka vishay ho - shared state ko dhakne ka tareeka nahi.

Assertions jo sahi failure jaanchein

assertThrows exception lautata hai, isliye type ke saath message bhi assert kijiye - rejectsMonthly exact text expected LPA, got: 50000 per month jaanchta hai. Jab code ummeed se alag type phenkta hai, failure bilkul saaf hota hai: Unexpected exception type thrown, expected: <java.lang.NullPointerException> but was: <java.lang.NumberFormatException>.

Ek niyam, kai input, padhne laayak report

@CsvSource ke saath @ParameterizedTest ne ek method ko teen alag report hone wale cases bana diya, har ek name pattern se naamit ("12 LPA" -> 1200000). Quote kiya hua ' 7 LPA ' dikhata hai ki CSV value me aage-peechhe ke space kaise bachayein. @Nested aur @DisplayName ne blank input wale cases ko ek padhne laayak heading ke neeche rakha, taaki tree ek specification jaisa padha jaaye.

JUnit 4 se JUnit 5 (documentation se - yahan chalaya nahi)

Is environment me JUnit 4 ka jar nahi hai, isliye ye mapping chala kar nahi, JUnit 5 documentation se hai: @Before/@After ab @BeforeEach/@AfterEach; @BeforeClass/@AfterClass ab @BeforeAll/@AfterAll; @RunWith(...) ab @ExtendWith(...) (pichhle topic ne @ExtendWith(MockitoExtension.class) chalaya tha); @Ignore ab @Disabled; @Test(expected = ...) ab assertThrows. Annotations org.junit se org.junit.jupiter.api me chale gaye.

@ParameterizedTest kab use karein: jab ek niyam inputs ki poori tabel par tikna chahiye - parsing, validation, seema wali values. Har row alag report hoti hai, isliye failure exact input ka naam leta hai.

@Nested kab use karein: jab test hone wali class ki alag-alag haalatein hon (blank input, sahi input, overflow) aur aap report bhi usi tarah bati hui chahte hon.

Kab NAHI @TestMethodOrder ya shared field use karna: ek test ko doosre ke bache hue data par tikaane ke liye. Naapa hua kram pehle hi source se alag hai; us par bharosa karne wali suite tab toot-ti hai jab koi naya method jodta hai.

Trade-off: error message khud @TestInstance(Lifecycle.PER_CLASS) ko non-static @BeforeAll ka raasta batata hai. JUnit documentation ke mutabik (yahan chalaya nahi) wo poori class ke liye ek hi instance use karta hai - mehnge setup ke liye aasaan, par upar naapa gaya har-test-naya-instance wala alagaav chhod deta hai.

// ---- t2/SalaryParser.java : "12 LPA" -> 1200000 rupees per year ----
public class SalaryParser {
  public static long parse(String text) {
    if (text == null || text.isBlank()) throw new IllegalArgumentException("salary is blank");
    String t = text.trim();
    if (!t.endsWith("LPA")) throw new IllegalArgumentException("expected LPA, got: " + t);
    double lakhs = Double.parseDouble(t.substring(0, t.length() - 3).trim());
    return Math.round(lakhs * 100_000);
  }
}

// ---- t2/LifecycleTest.java ----
class LifecycleTest {
  LifecycleTest() { System.out.println("  new LifecycleTest instance@" + Integer.toHexString(System.identityHashCode(this))); }
  @BeforeAll static void beforeAll() { System.out.println("@BeforeAll (static, once)"); }
  @BeforeEach void beforeEach() { System.out.println("  @BeforeEach on instance@" + Integer.toHexString(System.identityHashCode(this))); }
  @Test void first()  { System.out.println("    test first"); }
  @Test void second() { System.out.println("    test second"); }
  @AfterEach void afterEach() { System.out.println("  @AfterEach"); }
  @AfterAll static void afterAll() { System.out.println("@AfterAll (static, once)"); }
}

// ---- t2/SalaryParserTest.java ----
@DisplayName("SalaryParser")
class SalaryParserTest {
  @ParameterizedTest(name = "\"{0}\" -> {1}")
  @CsvSource({ "12 LPA, 1200000", "4.5 LPA, 450000", "' 7 LPA ', 700000" })
  void parsesLakhs(String text, long expected) { assertEquals(expected, SalaryParser.parse(text)); }

  @Test @DisplayName("rejects a monthly figure, with a useful message")
  void rejectsMonthly() {
    IllegalArgumentException e = assertThrows(IllegalArgumentException.class, () -> SalaryParser.parse("50000 per month"));
    assertEquals("expected LPA, got: 50000 per month", e.getMessage());
  }

  @Nested @DisplayName("when the input is blank")
  class WhenBlank {
    @Test @DisplayName("null is rejected")  void nullInput()  { assertThrows(IllegalArgumentException.class, () -> SalaryParser.parse(null)); }
    @Test @DisplayName("spaces are rejected") void spaces()   { assertThrows(IllegalArgumentException.class, () -> SalaryParser.parse("   ")); }
  }
}

// ---- t2bad/NonStaticBeforeAllTest.java ----
class NonStaticBeforeAllTest {
  @BeforeAll void setUp() { }          // not static
  @Test void anything() { }
}

// ---- t2bad/WrongExceptionTest.java ----
class WrongExceptionTest {
  @Test void expectsNpeButGetsNumberFormat() {
    assertThrows(NullPointerException.class, () -> Double.parseDouble("twelve"));
  }
}

// Each class run separately with the JUnit Platform 1.12.2 console launcher (Jupiter 5.12.2, JDK 17).

Project: Secure Job Board API ke tests likho

Spring Security chapter ne ek job board API secure ki thi aur har rule chalti server ko haath se bula kar sabit kiya tha: anonymous POST 401, plain user 403, recruiter 200, admin delete 200. Wo saboot chhapte hi purana ho gaya. Ye project wahi saboot ek test suite ki tarah likhta hai jo har build par chalti hai - aur phir configuration ko do baar todta hai taaki dikhe ki theek kaunse tests dhyaan dete hain.

Test hone wale rules

Endpoint Rule
GET /api/jobs public
POST /api/jobs sirf RECRUITER
DELETE /api/jobs/** sirf ADMIN
GET /api/jobs/mine apna koi rule nahi - anyRequest().authenticated() tak jaata hai

SecurityConfig apni @Configuration class hai, application class par @Bean methods ka set nahi. Transactional-pitfalls wale topic ne wajah naapi thi: main class ke beans har test slice me load hote hain, aur web security bean JPA slice tod deta hai.

Har rule ke liye ek ijaazat wala aur ek denial test

JobBoardRulesTest controller ka @Import(SecurityConfig.class) ke saath @WebMvcTest hai, aur har "kaun kar sakta hai" ko "kaun nahi kar sakta" ke saath jodta hai:

  • feed: anonymous GET 200;
  • posting: anonymous 401, USER 403, RECRUITER 200 - aur jsonPath("$.postedBy") rec hai, jo sabit karta hai ki job caller ki identity ki hai, kisi request parameter ki nahi;
  • deleting: RECRUITER 403, ADMIN 200;
  • "mine": anonymous 401; aur recruiter ki job recruiter ki list me dikhti hai par asha ki me nahi.

Run 1: saare 8 green. Abhi tak ye sirf wahi hai jo haath ki jaanch pehle dikha chuki thi.

Todna 1 - ek rule dheela karo

-Ddemo.widen=true delete rule ko hasRole("ADMIN") se hasAnyRole("ADMIN", "RECRUITER") kar deta hai - ek line ka wo badlav jo review se nikal jaata hai. Run 2: 7 ab bhi green, adminCanDelete samet. Theek ek test fail hua:

recruiterCannotDelete() [X] Status expected:<403> but was:<200>

Denial tests ki poori daleel yahi hai. "Admin delete kar sakta hai" wala test us rule ko notice nahi kar sakta jo zyada logon ko andar aane de. Sirf wahi test kar sakta hai jo rule se kisi ko mana karwaye.

Todna 2 - import bhool jaao

NoImportRulesTest wahi feed test hai bina @Import(SecurityConfig.class) ke. Run 3:

feedIsPublic() [X] Status expected:<200> but was:<401>

Import ke bina slice controller ko Spring Boot ke default chain se secure karta hai (test-slices wale topic me naapa), jahan kuch bhi public nahi hai. Yahan galti ittefaq se zor se dikhi. Chup wala roop bura hai: jo suite sirf authenticated calls test kare wo default chain par pass ho jaayegi aur aapka ek bhi rule test nahi karegi.

@WithMockUser ne kya chhoda

Upar ke har MockMvc test ne security context me ek taiyaar user rakha. Kisi ne nahi jaancha ki rec ka BCrypt password sach me chalta hai. RealCredentialsTest random port par asli server start karta hai aur asli HTTP Basic credentials bhejta hai: rec:pw2 200, rec:nope 401, asha:pw1 403. Ye authentication ka raasta hai - UserDetailsService, PasswordEncoder, Basic filter - shuru se aakhir tak, ek baar. Rule tests slice me tez rehte hain; ek asli-HTTP test wo cover karta hai jo wo nahi kar sakte.

Saath le jaane wali shakal

Har access rule ke liye kam se kam ek test rakhiye jo mana hone (401 ya 403) ki ummeed kare, asli security configuration slice me import kijiye, aur kuch asli-credential tests HTTP par jodiye. Saath milkar ye tab fail hote hain jab rule chaudaa ho, jab configuration gayab ho, aur jab authentication khud toote - wo teen tareeke jinse secured API chup-chaap secured rehna chhod deti hai.

// ---- p16/JobController.java : the job board API from the Spring Security chapter, same behaviour ----
record Job(Long id, String title, String city, String postedBy, String status) {}

@RestController public class JobController {
  private final Map<Long, Job> jobs = new ConcurrentHashMap<>();
  private final AtomicLong seq = new AtomicLong();
  @GetMapping("/api/jobs") public Collection<Job> feed() { return jobs.values(); }
  @PostMapping("/api/jobs") public Job post(@RequestParam String title, @RequestParam String city, Authentication a) {
    Job j = new Job(seq.incrementAndGet(), title, city, a.getName(), "OPEN"); jobs.put(j.id(), j); return j;
  }
  @GetMapping("/api/jobs/mine") public List<Job> mine(Authentication a) {
    return jobs.values().stream().filter(j -> j.postedBy().equals(a.getName())).toList();
  }
  @DeleteMapping("/api/jobs/{id}") public String delete(@PathVariable Long id) { return jobs.remove(id) == null ? "not-found" : "deleted"; }
}

// ---- p16/SecurityConfig.java : its own @Configuration, NOT on the main class ----
@Configuration public class SecurityConfig {
  @Bean SecurityFilterChain chain(HttpSecurity h) throws Exception {
    boolean widened = Boolean.getBoolean("demo.widen");          // simulates a careless edit to one rule
    return h.csrf(c -> c.disable())
        .sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
        .authorizeHttpRequests(a -> {
          a.requestMatchers(HttpMethod.GET, "/api/jobs").permitAll();
          a.requestMatchers(HttpMethod.POST, "/api/jobs").hasRole("RECRUITER");
          if (widened) a.requestMatchers(HttpMethod.DELETE, "/api/jobs/**").hasAnyRole("ADMIN", "RECRUITER");
          else         a.requestMatchers(HttpMethod.DELETE, "/api/jobs/**").hasRole("ADMIN");
          a.anyRequest().authenticated();
        })
        .httpBasic(b -> {}).build();
  }
  @Bean PasswordEncoder enc() { return new BCryptPasswordEncoder(); }
  @Bean UserDetailsService users(PasswordEncoder e) {
    return new InMemoryUserDetailsManager(
        User.withUsername("asha").password(e.encode("pw1")).roles("USER").build(),
        User.withUsername("rec").password(e.encode("pw2")).roles("RECRUITER").build(),
        User.withUsername("boss").password(e.encode("pw3")).roles("ADMIN").build());
  }
}

// ---- p16/JobBoardRulesTest.java : one allowed test and one denial test per rule ----
@WebMvcTest(JobController.class)
@Import(SecurityConfig.class)
class JobBoardRulesTest {
  @Autowired MockMvc mvc;

  // GET /api/jobs : public
  @Test void feedIsPublic() throws Exception { mvc.perform(get("/api/jobs")).andExpect(status().isOk()); }

  // POST /api/jobs : RECRUITER only
  @Test void anonymousCannotPost() throws Exception {
    mvc.perform(post("/api/jobs").param("title", "Java Dev").param("city", "Pune")).andExpect(status().isUnauthorized());
  }
  @Test @WithMockUser(username = "asha", roles = "USER") void userCannotPost() throws Exception {
    mvc.perform(post("/api/jobs").param("title", "Java Dev").param("city", "Pune")).andExpect(status().isForbidden());
  }
  @Test @WithMockUser(username = "rec", roles = "RECRUITER") void recruiterCanPostAndOwnsTheJob() throws Exception {
    mvc.perform(post("/api/jobs").param("title", "Java Dev").param("city", "Pune"))
       .andExpect(status().isOk()).andExpect(jsonPath("$.postedBy").value("rec"));
  }

  // DELETE /api/jobs/** : ADMIN only
  @Test @WithMockUser(username = "rec", roles = "RECRUITER") void recruiterCannotDelete() throws Exception {
    mvc.perform(delete("/api/jobs/1")).andExpect(status().isForbidden());
  }
  @Test @WithMockUser(username = "boss", roles = "ADMIN") void adminCanDelete() throws Exception {
    mvc.perform(delete("/api/jobs/999")).andExpect(status().isOk()).andExpect(content().string("not-found"));
  }

  // GET /api/jobs/mine : no rule of its own, falls to anyRequest().authenticated()
  @Test void anonymousCannotSeeMine() throws Exception { mvc.perform(get("/api/jobs/mine")).andExpect(status().isUnauthorized()); }
  @Test void mineShowsOnlyTheCallersJobs() throws Exception {
    mvc.perform(post("/api/jobs").param("title", "Rec Only Role").param("city", "Delhi").with(user("rec").roles("RECRUITER")))
       .andExpect(status().isOk());
    mvc.perform(get("/api/jobs/mine").with(user("rec").roles("RECRUITER")))
       .andExpect(jsonPath("$[*].title", hasItem("Rec Only Role")));
    mvc.perform(get("/api/jobs/mine").with(user("asha").roles("USER")))
       .andExpect(jsonPath("$[*].title", not(hasItem("Rec Only Role"))));
  }
}

// ---- p16/NoImportRulesTest.java ----
@WebMvcTest(JobController.class)            // @Import(SecurityConfig.class) forgotten
class NoImportRulesTest {
  @Autowired MockMvc mvc;
  @Test void feedIsPublic() throws Exception { mvc.perform(get("/api/jobs")).andExpect(status().isOk()); }
}

// ---- p16/RealCredentialsTest.java : the part @WithMockUser skips ----
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class RealCredentialsTest {
  @Autowired TestRestTemplate http;
  @Test void bcryptUsersOverRealHttp() {
    int good  = http.withBasicAuth("rec", "pw2").postForEntity("/api/jobs?title=JavaDev&city=Pune", null, String.class).getStatusCode().value();
    int wrong = http.withBasicAuth("rec", "nope").postForEntity("/api/jobs?title=JavaDev&city=Pune", null, String.class).getStatusCode().value();
    int user  = http.withBasicAuth("asha", "pw1").postForEntity("/api/jobs?title=JavaDev&city=Pune", null, String.class).getStatusCode().value();
    System.out.println("P16 real HTTP POST: rec:pw2 -> " + good + " | rec:nope -> " + wrong + " | asha:pw1 -> " + user);
    assertEquals(200, good); assertEquals(401, wrong); assertEquals(403, user);
  }
}

// run 1: JobBoardRulesTest   run 2: same with -Ddemo.widen=true   run 3: NoImportRulesTest   run 4: RealCredentialsTest
// Boot 3.5.16, Spring Security 6.5.11 + spring-security-test, JUnit 5.12.2, JDK 17.

Spring Testing interview questions & answers

10 sample questions below — 203+ in the full bank inside.

Mockito me @Mock, @Spy aur @InjectMocks me kya farak hai?

@Mock bina bartaav ka nakli banata hai: stub karne tak har method default lautata hai. @Spy asli object ko lapet-ta hai - calls asli code chalati hain par record hoti hain, isliye verify ya chuni hui jagah override ho sakti hain. @InjectMocks test hone wali class banata hai aur mocks aur spies usme deta hai (course demo me constructor injection). @ExtendWith(MockitoExtension.class) ke saath ye fields har test se pehle bante hain, Spring ke bina.

In simple terms: Mockito ki buniyaadi shabdavali ka sawaal.

JSON POST ke liye buniyaadi MockMvc chain likhiye jo title field ke saath 201 lautaye.

mvc.perform(post("/api/jobs").contentType(MediaType.APPLICATION_JSON).content("{\"title\":\"Java Developer\",\"openings\":2}")).andExpect(status().isCreated()).andExpect(jsonPath("$.title").value("Java Developer")). perform mock request banata hai aur DispatcherServlet ko bhejta hai; har andExpect response ka ek pehlu jaanchta hai. Bilkul yahi shakal course demo me pass hui.

In simple terms: Roz ka MockMvc sawaal; chain par pakad ki ummeed hai.

@DataJpaTest kya load karta hai, aur kya chhod deta hai?

Ye persistence layer load karta hai: JPA aur Hibernate, aapki Spring Data repositories, entity scanning, TestEntityManager, aur ek database - default me embedded. Ye web layer aur aapke @Service beans chhod deta hai. Naapa: @Service Mailer slice me 0 beans gina gaya. Isliye @DataJpaTest queries aur mappings test karta hai, unhe bulane wala service logic nahi.

In simple terms: Buniyaadi slice sawaal; service ka bahar rehna vyavharik nateeja hai.

JUnit Platform, Jupiter aur Vintage kya hain, aur bina Maven ya Gradle ke tests kaise chal sakte hain?

Platform tests dhoondhta, chalata aur nateeje batata hai; Jupiter JUnit 5 annotations ka engine hai; Vintage wo engine hai jo JUnit 4 tests usi Platform par chalata hai. Build tools aur IDE tests Platform ke zariye chalate hain. Is course me tests Platform ke apne console launcher, junit-platform-console-standalone 1.12.2, se execute --select-class ... ke saath bina kisi build tool ke chalaye gaye - report tree ne JUnit Platform Suite, JUnit Jupiter aur JUnit Vintage ko jadon ki tarah dikhaya.

In simple terms: Engine model samajhna batata hai ki JUnit 4 aur 5 tests ek run me saath kyun reh sakte hain.

Mockito me mock ko stub kaise karte hain aur interaction verify kaise karte hain?

when(gateway.charge("u1", 99)).thenReturn("rcpt-7") se stub kijiye, ya BDD roop given(gateway.charge("u1", 99)).willReturn("rcpt-7"). Test hone wala code bulane ke baad verify(gateway, times(1)).charge("u1", 99) se verify kijiye; verify(mailer, never()).send(anyString(), anyString()) assert karta hai ki call nahi hui. Demo me upgrade ne rcpt-7 lautaya aur dono verifications pass hue.

In simple terms: Buniyaadi API sawaal; never() ka zikr dikhata hai ki candidate interaction na hone ko bhi test karta hai.

Mockito ke @Mock aur Spring ke @MockitoBean (ya @MockBean) me kya farak hai?

@Mock ek aisa Mockito object banata hai jiske baare me sirf test jaanta hai - MockitoExtension wale plain unit tests me kaam ka. @MockitoBean (aur purana @MockBean) mock banata hai aur use Spring application context me rakhta hai, us type ke bean ko badal kar, taaki us par nirbhar har bean ko mock mile. Spring test me sirf doosra hi badalta hai ki application ke beans kya dekhte hain.

In simple terms: Spring testing ki ek bahut aam galti ke peechhe ka buniyaadi farak.

Bahut saari Spring teams JUnit ke assertEquals ki jagah AssertJ ka assertThat kyun pasand karti hain?

AssertJ test hone wali value se shuru hota hai - assertThat(actual).isEqualTo(expected) - isliye expected value ke liye ek hi jagah hai aur wo ulti nahi ho sakti. Isme collections, exceptions aur objects ke liye type samajhne wale assertions hain (containsExactly, extracting), ye check ke kram me padha jaata hai, aur behtar failure messages deta hai: galat kram wali list par isne poori actual list, poori expected list aur theek alag indexes chhape. Ye spring-boot-starter-test me aata hai, isliye extra dependency nahi.

In simple terms: Aam pasand wala sawaal; sabse achha jawab failure messages aur na ho sakne wale ulat-pher ke baare me hai, pasand ke nahi.

Spring TestContext ka context cache kya hai?

Ek JVM ke andar test framework ka rakha application contexts ka cache. Jab koi test class pehle se bane context jaisi bilkul ek jaisi configuration maangti hai, to wo application dobara start karne ki jagah wahi context reuse karti hai. Naapa: same configuration wali do test classes ne same context identity chhapi, ek component ka constructor ek baar chala, aur cache log ne missCount = 1 dikhaya. Bahut saare integration tests wali suite isi reuse se sasti padti hai.

In simple terms: Spring me test-speed ki har charcha ki neenv.

Do test methods wali JUnit 5 test class me @BeforeAll, @BeforeEach, @Test, @AfterEach aur @AfterAll kis kram me chalte hain?

@BeforeAll ek baar, sabse pehle. Phir har test method ke liye: test class ka naya instance banta hai, us par @BeforeEach chalta hai, test chalta hai, @AfterEach chalta hai. Aakhri test ke baad @AfterAll ek baar. Naape gaye run me do test methods ne do alag instance ids di, har ek apne @BeforeEach/@AfterEach se lipta hua, aur static @BeforeAll aur @AfterAll kinaaron par theek ek-ek baar chhape.

In simple terms: Buniyaadi lifecycle sawaal, par har method ka naya instance wo detail hai jo rata hua jawab samjhe hue jawab se alag karta hai.

Repository test suite apna data kaise taiyaar karti hai, aur har test zero jobs se kyun shuru hota hai?

@BeforeEach TestEntityManager se ek company aur paanch jobs persist karta hai, phir em.flush() aur em.clear() bulata hai. Seed har test ki transaction ke andar chalta hai, jise @DataJpaTest test ke aakhir me rollback karta hai, isliye har test ne seed se pehle jobs at start of test = 0 chhapa. Paanch tests ne bina cleanup code ke paanch baar seed kiya aur ek-doosre ki rows nahi dekhi.

In simple terms: Wo setup pattern samjhata hai jis par poora project tika hai.

193+ more Spring Testing questions inside

Start free to read the first 10 with model answers and take a voice mock interview. The full question bank comes with Pro.

Start free
Spring Testing Interview Questions & Answers | Hirenix