Backend Development 14 min read

Mastering Unit Testing with TestNG and PowerMock in Spring Boot

This article explains unit testing concepts, introduces TestNG and PowerMock frameworks for Spring Boot, details common annotations, provides Maven dependency snippets and comprehensive Java test examples, and demonstrates advanced mocking techniques, parameterized tests, and assertion strategies to improve code reliability and coverage.

Ops Development Stories
Ops Development Stories
Ops Development Stories
Mastering Unit Testing with TestNG and PowerMock in Spring Boot

Unit testing (Unit Testing) refers to verifying the correctness of the smallest testable units in software, such as a module, function, or class, performed in isolation from other modules.

After development, we cannot guarantee 100% correctness. By writing unit tests, we define input‑output behavior, use assertions to check each case, and improve correctness, stability, reliability, and development speed. Common frameworks include

Spring-Boot-Test

,

TestNG

, and

PowerMock

.

TestNG

(Testing, Next Generation) is a next‑generation testing framework built on JUnit and NUnit ideas, supporting both unit and integration testing via annotations.

PowerMock

extends other mocking frameworks, providing custom class loaders and bytecode manipulation to mock static, constructor, private, and final methods.

Common Annotations

1. TestNG Annotations

@BeforeSuite

runs once before all tests in the suite.

@AfterSuite

runs once after all tests in the suite.

@BeforeClass

runs once before the first test method in the class.

@AfterClass

runs once after the last test method in the class.

@BeforeMethod

runs before each test method.

@AfterMethod

runs after each test method.

@BeforeTest

runs before all test methods inside a

<test>

tag.

@AfterTest

runs after all test methods inside a

<test>

tag.

@DataProvider

supplies a two‑dimensional

Object[][]

data source for

@Test

methods.

@Parameters

passes parameters from XML configuration to

@Test

methods.

@Test

marks a class or method as a test case.

2. PowerMock Annotations

@Mock

is a shortcut for

Mockito.mock()

.

@InjectMocks

injects existing mock objects into a bean by name.

@Spy

wraps a real object to allow behavior verification.

Example Code

1. Add pom.xml Dependencies

<code>&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
    &lt;artifactId&gt;spring-boot-starter-test&lt;/artifactId&gt;
    &lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
    &lt;groupId&gt;org.testng&lt;/groupId&gt;
    &lt;artifactId&gt;testng&lt;/artifactId&gt;
    &lt;version&gt;${testng.version}&lt;/version&gt;
    &lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
    &lt;groupId&gt;org.powermock&lt;/groupId&gt;
    &lt;artifactId&gt;powermock-api-mockito2&lt;/artifactId&gt;
    &lt;version&gt;${powermock.version}&lt;/version&gt;
    &lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
    &lt;groupId&gt;org.powermock&lt;/groupId&gt;
    &lt;artifactId&gt;powermock-module-junit4&lt;/artifactId&gt;
    &lt;version&gt;${powermock.version}&lt;/version&gt;
    &lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
    &lt;groupId&gt;org.powermock&lt;/groupId&gt;
    &lt;artifactId&gt;powermock-module-testng&lt;/artifactId&gt;
    &lt;version&gt;${powermock.version}&lt;/version&gt;
    &lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;</code>

2. Add Unit Tests

<code>import com.test.testng.dto.OrderDto;
import com.test.testng.dto.UserDto;
import org.mockito.*;
import org.powermock.modules.testng.PowerMockTestCase;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.when;

public class OrderServiceTest extends PowerMockTestCase {
    @BeforeMethod
    public void before() {
        MockitoAnnotations.openMocks(this);
    }
    @InjectMocks
    private OrderService orderService;
    @Mock
    private UserService userService;
    // Normal test
    @Test
    public void testCreateOrder() {
        UserDto userDto = new UserDto();
        userDto.setId(100);
        when(userService.get()).thenReturn(userDto);
        OrderDto order = orderService.createOrder(new OrderDto());
        assertEquals(order.getId(), 100);
    }
    // Exception test
    @Test
    public void testCreateOrderEx() {
        when(userService.get()).thenThrow(new RuntimeException());
        Exception exception = null;
        try {
            orderService.createOrder(new OrderDto());
        } catch (RuntimeException e) {
            exception = e;
        }
        assertNotNull(exception);
    }
}
</code>

Common Mock Methods

1. Mock Static Methods

<code>// static method
UserDto dto = new UserDto();
 dto.setId(100000);
PowerMockito.mockStatic(UserService.class);
PowerMockito.when(UserService.loginStatic()).thenReturn(dto);
UserDto userDto = UserService.loginStatic();
assertEquals(100000, userDto.getId().intValue());</code>

2. Mock Private Fields

<code>// field injection
ReflectionTestUtils.setField(orderService, "rateLimit", 99);</code>

3. Mock Private Methods

<code>// mock private method
MemberModifier.stub(MemberMatcher.method(UserService.class, "get1")).toReturn(new UserDto());
// invoke private method
Method method = PowerMockito.method(UserService.class, "get1", Integer.class);
Object userDto = method.invoke(userService, 1);
assertTrue(userDto instanceof UserDto);
</code>

Advanced Usage

1. Parameterized Batch Testing

When test data is large, use

@DataProvider

to generate a data source and reference it with

@Test(dataProvider = "xxx")

:

<code>import com.test.testng.BaseTest;
import com.test.testng.dto.UserDto;
import org.mockito.InjectMocks;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import static org.testng.Assert.assertFalse;
import static org.testng.AssertJUnit.assertTrue;

public class UserServiceTest2 extends BaseTest {
    @InjectMocks
    private UserService userService;
    @DataProvider(name = "test")
    public static Object[][] userList() {
        UserDto dto1 = new UserDto();
        UserDto dto2 = new UserDto();
        dto2.setSex(1);
        UserDto dto3 = new UserDto();
        dto3.setSex(1);
        dto3.setFlag(1);
        UserDto dto4 = new UserDto();
        dto4.setSex(1);
        dto4.setFlag(1);
        dto4.setAge(1);
        return new Object[][] {{dto1, null}, {dto2, null}, {dto3, null}, {dto4, null}};
    }
    @Test
    public void testCheckEffectiveUser() {
        UserDto dto = new UserDto();
        dto.setSex(1);
        dto.setFlag(1);
        dto.setAge(18);
        boolean result = userService.checkEffectiveUser(dto);
        assertTrue(result);
    }
    @Test(dataProvider = "test")
    public void testCheckEffectiveUser(UserDto dto, Object object) {
        boolean result = userService.checkEffectiveUser(dto);
        assertFalse(result);
    }
}
</code>

2. Complex Condition to Ensure Coverage

Effective user: age > 18, sex = 1, flag = 1.

<code>public boolean checkEffectiveUser(UserDto dto) {
    return Objects.equals(dto.getSex(), 1) &&
           Objects.equals(dto.getFlag(), 1) &&
           dto.getAge() != null && dto.getAge() >= 18;
}
</code>

Refactor to simple if‑else for full coverage:

<code>public boolean checkEffectiveUser(UserDto dto) {
    if (!Objects.equals(dto.getSex(), 1)) return false;
    if (!Objects.equals(dto.getFlag(), 1)) return false;
    if (dto.getAge() == null) return false;
    if (dto.getAge() < 18) return false;
    return true;
}
</code>

Corresponding unit tests covering each return path:

<code>public class UserServiceTest extends BaseTest {
    @InjectMocks
    private UserService userService;
    @Test public void testCheckEffectiveUser_0() { assertFalse(userService.checkEffectiveUser(new UserDto())); }
    @Test public void testCheckEffectiveUser_1() { UserDto d=new UserDto(); d.setSex(1); assertFalse(userService.checkEffectiveUser(d)); }
    @Test public void testCheckEffectiveUser_2() { UserDto d=new UserDto(); d.setSex(1); d.setFlag(1); assertFalse(userService.checkEffectiveUser(d)); }
    @Test public void testCheckEffectiveUser_3() { UserDto d=new UserDto(); d.setSex(1); d.setFlag(1); d.setAge(1); assertFalse(userService.checkEffectiveUser(d)); }
    @Test public void testCheckEffectiveUser_4() { UserDto d=new UserDto(); d.setSex(1); d.setFlag(1); d.setAge(18); assertTrue(userService.checkEffectiveUser(d)); }
}
</code>

3. Assert Method Parameters

Java

assert

keyword for debugging.

<code>int a = 0, b = 1;
assert a == 0 && b == 0;
// compile with -ea to enable assertions
</code>

Spring

Assert

for validating incoming parameters.

<code>public boolean checkUserAge(UserDto dto){
    Assert.notNull(dto.getAge(), "User age cannot be null");
    Assert.isTrue(dto.getAge() >= 18, "User age must be at least 18");
    return true;
}
</code>

Unified REST API error handling.

<code>@ControllerAdvice
public class GlobalExceptionHandler {
    @ResponseBody
    @ExceptionHandler(IllegalArgumentException.class)
    public Response<String> handleArgError(IllegalArgumentException e){
        return new Response().failure().message(e.getMessage());
    }
}
</code>

Summary

When designing functional modules, follow principles such as moderate module size, appropriate call depth, high cohesion and low coupling, single entry/exit, limited data redundancy, and layered system decomposition, as recommended by software engineering design guidelines.

References

https://testng.org/doc/

https://github.com/powermock/powermock

https://www.netconcepts.cn/detail-41004.html

Unit testing coverage diagram
Unit testing coverage diagram
Javaunit testingmockingSpring BootPowerMockTestNG
Ops Development Stories
Written by

Ops Development Stories

Maintained by a like‑minded team, covering both operations and development. Topics span Linux ops, DevOps toolchain, Kubernetes containerization, monitoring, log collection, network security, and Python or Go development. Team members: Qiao Ke, wanger, Dong Ge, Su Xin, Hua Zai, Zheng Ge, Teacher Xia.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.