When talking about methodology, Xiang, a technology expert of AMAP said, "Complicated problems should be simplified while the simple ones should be deepened."
This statement deeply touched me, and I believe it also applies to writing code. In other words, after splitting a complex logic into lots of simple logic, and then implementing each simple logic in-depth, the simple logic is integrated into complex logic. The quote above can be summed up as, "changing complexity into simplicity, from simplicity to complexity."
Writing Java unit testing cases simplifies complex problems, breaking down a complex piece of code into a series of simple unit testing cases. Writing Java unit testing cases lessens simple problems by learning a set of methods, summarizing a set of patterns, and applying it to the practice. Some Java unit testing techniques have been summarized below based on my work experience for communication and study purposes.
PowerMock is a more powerful framework that extends other mock frameworks, such as EasyMock. PowerMock uses a custom class loader and bytecode manipulation to simulate methods, including static methods, constructors, class's methods final, and private methods. Remove static initializers can be simulated as well.
Add the following maven dependency to the pom.xml file to introduce the PowerMock package:
In the SpringMVC project, add the following maven dependency of JUnit to the pom.xml file:
In the SpringBoot project, add the following maven dependency of the JUnit to the pom.xml file:
Here, take the list below as an example to simulate a non-existent list. The returned list size is 100.
public class ListTest {
@Test
public void testSize() {
Integer expected = 100;
List list = PowerMockito.mock(List.class);
PowerMockito.when(list.size()).thenReturn(expected);
Integer actual = list.size();
Assert.assertEquals("Return values are not equal", expected, actual);
}
}
Statement:
T PowerMockito.mock(Class clazz)
Purpose:
It can be used to simulate an object instance of a specified class.
When simulating a non-final method of a non-final class, such as interface, common class, and virtual base class, users do not need to use @RunWith and @PrepareForTest. When simulating the final class or final method, users must @RunWith and @PrepareForTest.
The forms of the annotations above are:
@RunWith(PowerMockRunner.class)
@PrepareForTest({TargetClass.class})
@Getter
@Setter
@ToString
public class Rectangle implements Sharp {
private double width;
private double height;
@Override
public double getArea() {
return width * height;
}
}
public class RectangleTest {
@Test
public void testGetArea() {
double expectArea = 100.0D;
Rectangle rectangle = PowerMockito.mock(Rectangle.class);
PowerMockito.when(rectangle.getArea()).thenReturn(expectArea);
double actualArea = rectangle.getArea();
Assert.assertEquals("Return values are not equal", expectArea, actualArea, 1E-6D);
}
}
@Getter
@Setter
@ToString
public final class Circle {
private double radius;
public double getArea() {
return Math.PI * Math.pow(radius, 2);
}
}
@RunWith(PowerMockRunner.class)
@PrepareForTest({Circle.class})
public class CircleTest {
@Test
public void testGetArea() {
double expectArea = 3.14D;
Circle circle = PowerMockito.mock(Circle.class);
PowerMockito.when(circle.getArea()).thenReturn(expectArea);
double actualArea = circle.getArea();
Assert.assertEquals("Return values are not equal", expectArea, actualArea, 1E-6D);
}
}
Statement:
PowerMockito.mockStatic(Class clazz)
Purpose:
It can be used for static methods under simulate classes. Users must use @RunWith and @PrepareForTest.
@RunWith(PowerMockRunner.class)
@PrepareForTest({StringUtils.class})
public class StringUtilsTest {
@Test
public void testIsEmpty() {
String string = "abc";
boolean expected = true;
PowerMockito.mockStatic(StringUtils.class);
PowerMockito.when(StringUtils.isEmpty(string)).thenReturn(expected);
boolean actual = StringUtils.isEmpty(string);
Assert.assertEquals("Return values are not equal", expected, actual);
}
}
For an object, if users only want to simulate some of its methods and the other methods to be the same as the original, they can use the PowerMockito.spy instead of the PowerMockito.mock. Methods that have been set by the when the statement are called simulation methods, while those without the when statement are called original methods.
Statement:
PowerMockito.spy(Class clazz)
Purpose:
It can be used for partial methods of the simulation class.
Case:
public class StringUtils {
public static boolean isNotEmpty(final CharSequence cs) {
return !isEmpty(cs);
}
public static boolean isEmpty(final CharSequence cs) {
return cs == null || cs.length() == 0;
}
}
@RunWith(PowerMockRunner.class)
@PrepareForTest({StringUtils.class})
public class StringUtilsTest {
@Test
public void testIsNotEmpty() {
String string = null;
boolean expected = true;
PowerMockito.spy(StringUtils.class);
PowerMockito.when(StringUtils.isEmpty(string)).thenReturn(!expected);
boolean actual = StringUtils.isNotEmpty(string);
Assert.assertEquals("Return values are not equal", expected, actual);
}
}
Statement:
T PowerMockito.spy(T object)
Purpose:
It can be used for partial methods of mock objects.
Case:
public class UserService {
private Long superUserId;
public boolean isNotSuperUser(Long userId) {
return !isSuperUser(userId);
}
public boolean isSuperUser(Long userId) {
return Objects.equals(userId, superUserId);
}
}
@RunWith(PowerMockRunner.class)
public class UserServiceTest {
@Test
public void testIsNotSuperUser() {
Long userId = 1L;
boolean expected = false;
UserService userService = PowerMockito.spy(new UserService());
PowerMockito.when(userService.isSuperUser(userId)).thenReturn(!expected);
boolean actual = userService.isNotSuperUser(userId);
Assert.assertEquals("Return values are not equal", expected, actual);
}
}
When(). Thirreturn ()
ModeStatement:
PowerMockito.when(mockObject.someMethod(someArgs)).thenReturn(expectedValue)
;
PowerMockito.when(mockObject.someMethod(someArgs)).thenThrow(expectedThrowable)
;
PowerMockito.when(mockObject.someMethod(someArgs)).thenAnswer(expectedAnswer)
;
PowerMockito.when(mockObject.someMethod(someArgs)).thenCallRealMethod()
.
Purpose:
It can be used to mock objects methods. Execute the original method first. Then, return the expected value, exception, response, or call real methods.
public class ListTest {
@Test
public void testGet() {
int index = 0;
Integer expected = 100;
List<Integer> mockList = PowerMockito.mock(List.class);
PowerMockito.when(mockList.get(index)).thenReturn(expected);
Integer actual = mockList.get(index);
Assert.assertEquals("Return values are not equal", expected, actual);
}
}
public class ListTest {
@Test(expected = IndexOutOfBoundsException.class)
public void testGet() {
int index = -1;
Integer expected = 100;
List<Integer> mockList = PowerMockito.mock(List.class);
PowerMockito.when(mockList.get(index)).thenThrow(new IndexOutOfBoundsException());
Integer actual = mockList.get(index);
Assert.assertEquals("Return values are not equal", expected, actual);
}
}
public class ListTest {
@Test
public void testGet() {
int index = 1;
Integer expected = 100;
List<Integer> mockList = PowerMockito.mock(List.class);
PowerMockito.when(mockList.get(index)).thenAnswer(invocation -> {
Integer value = invocation.getArgument(0);
return value * 100;
});
Integer actual = mockList.get(index);
Assert.assertEquals("Return values are not equal", expected, actual);
}
}
public class ListTest {
@Test
public void testGet() {
int index = 0;
Integer expected = 100;
List<Integer> oldList = new ArrayList<>();
oldList.add(expected);
List<Integer> spylist = PowerMockito.spy(oldList);
PowerMockito.when(spylist.get(index)).thenCallRealMethod();
Integer actual = spylist.get(index);
Assert.assertEquals("Return values are not equal", expected, actual);
}
}
DoReturn().when()
ModeStatement:
PowerMockito.doReturn(expectedValue).when(mockObject).someMethod(someArgs)
;
PowerMockito.doThrow(expectedThrowable).when(mockObject).someMethod(someArgs)
;
PowerMockito.doAnswer(expectedAnswer).when(mockObject).someMethod(someArgs)
;
PowerMockito.doNothing().when(mockObject).someMethod(someArgs)
;
PowerMockito.doCallRealMethod().when(mockObject).someMethod(someArgs)
.
Purpose:
It can be used to mock objects methods. Return the expected value, exception, response, or call real methods without executing the original method
Note:
Do not use the following syntax:
PowerMockito.doReturn(expectedValue).when(mockObject.someMethod(someArgs))
;
PowerMockito.doThrow(expectedThrowable).when(mockObject.someMethod(someArgs))
;
PowerMockito.doAnswer(expectedAnswer).when(mockObject.someMethod(someArgs))
;
PowerMockito.doNothing().when(mockObject.someMethod(someArgs))
;
PowerMockito.doCallRealMethod().when(mockObject.someMethod(someArgs))
.
Although no compilation errors will occur, the UnfinishedStubbingException
will occur during execution.
public class ListTest {
@Test
public void testGet() {
int index = 0;
Integer expected = 100;
List<Integer> mockList = PowerMockito.mock(List.class);
PowerMockito.doReturn(expected).when(mockList).get(index);
Integer actual = mockList.get(index);
Assert.assertEquals("Return values are not equal", expected, actual);
}
}
public class ListTest {
@Test(expected = IndexOutOfBoundsException.class)
public void testGet() {
int index = -1;
Integer expected = 100;
List<Integer> mockList = PowerMockito.mock(List.class);
PowerMockito.doThrow(new IndexOutOfBoundsException()).when(mockList).get(index);
Integer actual = mockList.get(index);
Assert.assertEquals("Return values are not equal", expected, actual);
}
}
public class ListTest {
@Test
public void testGet() {
int index = 1;
Integer expected = 100;
List<Integer> mockList = PowerMockito.mock(List.class);
PowerMockito.doAnswer(invocation -> {
Integer value = invocation.getArgument(0);
return value * 100;
}).when(mockList).get(index);
Integer actual = mockList.get(index);
Assert.assertEquals("Return values are not equal", expected, actual);
}
}
public class ListTest {
@Test
public void testClear() {
List<Integer> mockList = PowerMockito.mock(List.class);
PowerMockito.doNothing().when(mockList).clear();
mockList.clear();
Mockito.verify(mockList).clear();
}
}
public class ListTest {
@Test
public void testGet() {
int index = 0;
Integer expected = 100;
List<Integer> oldList = new ArrayList<>();
oldList.add(expected);
List<Integer> spylist = PowerMockito.spy(oldList);
PowerMockito.doCallRealMethod().when(spylist).get(index);
Integer actual = spylist.get(index);
Assert.assertEquals("Return values are not equal", expected, actual);
}
}
Both modes are used to mock object methods, and there is almost no difference when used under mock instances. However, when used with spy instances, the when().thenReturn()
executes the original method, but the doReturn().when()
does not.
Service Testing:
@Slf4j
@Service
public class UserService {
public long getUserCount() {
log.info("Call the methods of obtaining the number of users");
return 0L;
}
}
Testing under the when().thenReturn()
mode:
@RunWith(PowerMockRunner.class)
public class UserServiceTest {
@Test
public void testGetUserCount() {
Long expected = 1000L;
UserService userService = PowerMockito.spy(new UserService());
PowerMockito.when(userService.getUserCount()).thenReturn(expected);
Long actual = userService.getUserCount();
Assert.assertEquals("Return values are not equal", expected, actual);
}
}
During the test, the "call the methods of obtaining the number of users" log will be printed.
Testing under the doReturn().when()
mode:
@RunWith(PowerMockRunner.class)
public class UserServiceTest {
@Test
public void testGetUserCount() {
Long expected = 1000L;
UserService userService = PowerMockito.spy(new UserService());
PowerMockito.doReturn(expected).when(userService).getUserCount();
Long actual = userService.getUserCount();
Assert.assertEquals("Return values are not equal", expected, actual);
}
}
During the test, the "call the methods of obtaining the number of users" log will not be printed.
Statement:
PowerMockito.whenNew(MockClass.class).withNoArguments().thenReturn(expectedObject)
;
PowerMockito.whenNew(MockClass.class).withArguments(someArgs).thenReturn(expectedObject)
.
Purpose:
It is used to simulate the construction method.
Case:
public final class FileUtils {
public static boolean isFile(String fileName) {
return new File(fileName).isFile();
}
}
@RunWith(PowerMockRunner.class)
@PrepareForTest({FileUtils.class})
public class FileUtilsTest {
@Test
public void testIsFile() throws Exception {
String fileName = "test.txt";
File file = PowerMockito.mock(File.class);
PowerMockito.whenNew(File.class).withArguments(fileName).thenReturn(file);
PowerMockito.when(file.isFile()).thenReturn(true);
Assert.assertTrue("Return values are false", FileUtils.isFile(fileName));
}
}
Note: The @PrepareForTest({FileUtils.class}) is required. Otherwise, the simulation method does not take effect.
When executing unit testing that occasionally does not care about the values of the incoming parameters, users can use the argument matcher.
Mockito provides Mockito.anyInt()
, Mockito.anyString
, and Mockito.any(Class clazz)
to indicate arbitrary values.
public class ListTest {
@Test
public void testGet() {
int index = 1;
Integer expected = 100;
List<Integer> mockList = PowerMockito.mock(List.class);
PowerMockito.when(mockList.get(Mockito.anyInt())).thenReturn(expected);
Integer actual = mockList.get(index);
Assert.assertEquals("Return values are not equal", expected, actual);
}
}
When using the argument matcher, all parameters should be used in the matcher. If users want to specify a specific value for a parameter, the Mockito.eq()
can be applied.
@RunWith(PowerMockRunner.class)
@PrepareForTest({StringUtils.class})
public class StringUtilsTest {
@Test
public void testStartWith() {
String string = "abc";
String prefix = "b";
boolean expected = true;
PowerMockito.spy(StringUtils.class);
PowerMockito.when(StringUtils.startsWith(Mockito.anyString(), Mockito.eq(prefix))).thenReturn(expected);
boolean actual = StringUtils.startsWith(string, prefix);
Assert.assertEquals("Return values are not equal", expected, actual);
}
}
The AdditionalMatchers
class of Mockito provides some rarely used argument matchers. Users can perform comparison operations, such as parameters to be greater than (gt), less than (lt), greater than or equal to (geq), and less than or equal to (leq). Logical calculations, including and or and not can be performed as well.
public class ListTest {
@Test
public void testGet() {
int index = 1;
Integer expected = 100;
List<Integer> mockList = PowerMockito.mock(List.class);
PowerMockito.when(mockList.get(AdditionalMatchers.geq(0))).thenReturn(expected);
PowerMockito.when(mockList.get(AdditionalMatchers.lt(0))).thenThrow(new IndexOutOfBoundsException());
Integer actual = mockList.get(index);
Assert.assertEquals("Return values are not equal", expected, actual);
}
}
Verification confirms whether the method under the test has interacted with any of its dependent methods expectedly during the simulation.
Format:
Mockito.verify(mockObject[,times(int)]).someMethod(somgArgs)
Purpose:
It can be used to mock objects methods. Return the expected value, exception, response, or call real methods without executing the original method
Case:
public class ListTest {
@Test
public void testGet() {
List<Integer> mockList = PowerMockito.mock(List.class);
PowerMockito.doNothing().when(mockList).clear();
mockList.clear();
Mockito.verify(mockList).clear();
}
}
public class ListTest {
@Test
public void testGet() {
List<Integer> mockList = PowerMockito.mock(List.class);
PowerMockito.doNothing().when(mockList).clear();
mockList.clear();
Mockito.verify(mockList, Mockito.times(1)).clear();
}
}
In addition to times, Mockito supports verifiers, such as atLeastOnce
, atLeast
, only
, atMostOnce
, and atMost
.
public class ListTest {
@Test
public void testAdd() {
List<Integer> mockedList = PowerMockito.mock(List.class);
PowerMockito.doReturn(true).when(mockedList).add(Mockito.anyInt());
mockedList.add(1);
mockedList.add(2);
mockedList.add(3);
InOrder inOrder = Mockito.inOrder(mockedList);
inOrder.verify(mockedList).add(1);
inOrder.verify(mockedList).add(2);
inOrder.verify(mockedList).add(3);
}
}
public class ListTest {
@Test
public void testArgumentCaptor() {
Integer[] expecteds = new Integer[] {1, 2, 3};
List<Integer> mockedList = PowerMockito.mock(List.class);
PowerMockito.doReturn(true).when(mockedList).add(Mockito.anyInt());
for (Integer expected : expecteds) {
mockedList.add(expected);
}
ArgumentCaptor<Integer> argumentCaptor = ArgumentCaptor.forClass(Integer.class);
Mockito.verify(mockedList, Mockito.times(3)).add(argumentCaptor.capture());
Integer[] actuals = argumentCaptor.getAllValues().toArray(new Integer[0]);
Assert.assertArrayEquals("Return values are not equal", expecteds, actuals);
}
}
Mockito supports the Mockito.verifyNoMoreInteractions
, which can be used after all verification methods have been completed to ensure that all calls are verified. If there are any unverified calls to the mock objects, the nointeractionsprotected
exception will occur.
public class ListTest {
@Test
public void testVerifyNoMoreInteractions() {
List<Integer> mockedList = PowerMockito.mock(List.class);
Mockito.verifyNoMoreInteractions(mockedList); // Normal execution
mockedList.isEmpty();
Mockito.verifyNoMoreInteractions(mockedList); // Exception thrown
}
}
Note: The Mockito.verifyZeroInteractions
is the same as the Mockito.verifyNoMoreInteractions
, but the former has been phased out.
Mockito has no verification method for static methods, but PowerMock can provide support in this situation.
@RunWith(PowerMockRunner.class)
@PrepareForTest({StringUtils.class})
public class StringUtilsTest {
@Test
public void testVerifyStatic() {
PowerMockito.mockStatic(StringUtils.class);
String expected = "abc";
StringUtils.isEmpty(expected);
PowerMockito.verifyStatic(StringUtils.class);
ArgumentCaptor<String> argumentCaptor = ArgumentCaptor.forClass(String.class);
StringUtils.isEmpty(argumentCaptor.capture());
Assert.assertEquals("Parameters are not equal", argumentCaptor.getValue(), expected);
}
}
ReflectionTestUtils.setField
When using native JUnit for unit testing, the ReflectionTestUtils.setField
is generally performed to set the private property value.
@Service
public class UserService {
@Value("${system.userLimit}")
private Long userLimit;
public Long getUserLimit() {
return userLimit;
}
}
public class UserServiceTest {
@Autowired
private UserService userService;
@Test
public void testGetUserLimit() {
Long expected = 1000L;
ReflectionTestUtils.setField(userService, "userLimit", expected);
Long actual = userService.getUserLimit();
Assert.assertEquals("Return values are not equal", expected, actual);
}
}
Note: In the test class, the UserService instance is loaded using the @Autowired. If this instance is referenced by a dynamic proxy, the ReflectionTestUtils.setField
sets the proxy instance, resulting in the settings not taking effect.
Whitebox.setInternalState
When using PowerMock for unit tests, Whitebox.setInternalState
can be performed to set the private property value.
@Service
public class UserService {
@Value("${system.userLimit}")
private Long userLimit;
public Long getUserLimit() {
return userLimit;
}
}
@RunWith(PowerMockRunner.class)
public class UserServiceTest {
@InjectMocks
private UserService userService;
@Test
public void testGetUserLimit() {
Long expected = 1000L;
Whitebox.setInternalState(userService, "userLimit", expected);
Long actual = userService.getUserLimit();
Assert.assertEquals("Return values are not equal", expected, actual);
}
}
Note: @RunWith (powermoccrunner. class) should be added.
public class UserService {
private Long superUserId;
public boolean isNotSuperUser(Long userId) {
return !isSuperUser(userId);
}
private boolean isSuperUser(Long userId) {
return Objects.equals(userId, superUserId);
}
}
@RunWith(PowerMockRunner.class)
@PrepareForTest({UserService.class})
public class UserServiceTest {
@Test
public void testIsNotSuperUser() throws Exception {
Long userId = 1L;
boolean expected = false;
UserService userService = PowerMockito.spy(new UserService());
PowerMockito.when(userService, "isSuperUser", userId).thenReturn(!expected);
boolean actual = userService.isNotSuperUser(userId);
Assert.assertEquals("Return values are not equal", expected, actual);
}
}
Private methods can also be implemented by simulating a method stub. However, only the return value of the entire method can be simulated, not the return value of the specified parameter.
@RunWith(PowerMockRunner.class)
@PrepareForTest({UserService.class})
public class UserServiceTest {
@Test
public void testIsNotSuperUser() throws Exception {
Long userId = 1L;
boolean expected = false;
UserService userService = PowerMockito.spy(new UserService());
PowerMockito.stub(PowerMockito.method(UserService.class, "isSuperUser", Long.class)).toReturn(!expected);
boolean actual = userService.isNotSuperUser(userId);
Assert.assertEquals("Return values are not equal", expected, actual;
}
}
@RunWith(PowerMockRunner.class)
public class UserServiceTest9 {
@Test
public void testIsSuperUser() throws Exception {
Long userId = 1L;
boolean expected = false;
UserService userService = new UserService();
Method method = PowerMockito.method(UserService.class, "isSuperUser", Long.class);
Object actual = method.invoke(userService, userId);
Assert.assertEquals("Return values are not equal", expected, actual);
}
}
@RunWith(PowerMockRunner.class)
@PrepareForTest({UserService.class})
public class UserServiceTest10 {
@Test
public void testIsNotSuperUser() throws Exception {
Long userId = 1L;
boolean expected = false;
UserService userService = PowerMockito.spy(new UserService());
PowerMockito.when(userService, "isSuperUser", userId).thenReturn(!expected);
boolean actual = userService.isNotSuperUser(userId);
PowerMockito.verifyPrivate(userService).invoke("isSuperUser", userId);
Assert.assertEquals("Return values are not equal", expected, actual);
}
}
Here, the method can also be used for simulation and verification.
PowerMock provides a series of annotations to better support the SpringMVC/SpringBoot project, which greatly simplifies the test code.
@RunWith(PowerMockRunner.class)
Specify that the JUnit uses the unit testing runner in the PowerMock framework
@PrepareForTest({ TargetClass.class })
Add the @PrepareForTest and specify the class where the method is located to simulate a final class, final method, or static method. Add classes in {} and separate them with commas to specify multiple classes.
The @Mock creates an instance of all mock, and all properties and methods are left blank with 0 or null.
The @Spy creates an instance without mock. All member methods will be executed according to the logic of the original method until a specific value is returned by mock.
Note: Variables with @Spy must be initialized, otherwise, an exception will occur during the execution.
The @InjectMocks creates an instance that can call the code. Other instances created with the @Mock or @Spy will be applied to this instance.
@Service
public class UserService {
@Autowired
private UserDAO userDAO;
public void modifyUser(UserVO userVO) {
UserDO userDO = new UserDO();
BeanUtils.copyProperties(userVO, userDO);
userDAO.modify(userDO);
}
}
@RunWith(PowerMockRunner.class)
public class UserServiceTest {
@Mock
private UserDAO userDAO;
@InjectMocks
private UserService userService;
@Test
public void testCreateUser() {
UserVO userVO = new UserVO();
userVO.setId(1L);
userVO.setName("changyi");
userVO.setDesc("test user");
userService.modifyUser(userVO);
ArgumentCaptor<UserDO> argumentCaptor = ArgumentCaptor.forClass(UserDO.class);
Mockito.verify(userDAO).modify(argumentCaptor.capture());
UserDO userDO = argumentCaptor.getValue();
Assert.assertNotNull("User instance is null", userDO);
Assert.assertEquals("User identifications are not equal", userVO.getId(), userDO.getId());
Assert.assertEquals("User names are not equal", userVO.getName(), userDO.getName());
Assert.assertEquals("User descriptions are not equal", userVO.getDesc(), userDO.getDesc());
}
}
The @Captor creates an ArgumentCaptor at the field level. However, before starting the test method, MockitoAnnotations.openMocks(this)
should be called for initialization.
@Service
public class UserService {
@Autowired
private UserDAO userDAO;
public void modifyUser(UserVO userVO) {
UserDO userDO = new UserDO();
BeanUtils.copyProperties(userVO, userDO);
userDAO.modify(userDO);
}
}
@RunWith(PowerMockRunner.class)
public class UserServiceTest {
@Mock
private UserDAO userDAO;
@InjectMocks
private UserService userService;
@Captor
private ArgumentCaptor<UserDO> argumentCaptor;
@Before
public void beforeTest() {
MockitoAnnotations.openMocks(this);
}
@Test
public void testCreateUser() {
UserVO userVO = new UserVO();
userVO.setId(1L);
userVO.setName("changyi");
userVO.setDesc("test user");
userService.modifyUser(userVO);
Mockito.verify(userDAO).modify(argumentCaptor.capture());
UserDO userDO = argumentCaptor.getValue();
Assert.assertNotNull("User instance is null", userDO);
Assert.assertEquals("User identifications are not equal", userVO.getId(), userDO.getId());
Assert.assertEquals("User names are not equal", userVO.getName(), userDO.getName());
Assert.assertEquals("User descriptions are not equal", userVO.getDesc(), userDO.getDesc());
}
}
@PowerMockIgnore solves the ClassLoader error after using PowerMock
Good unit testing must comply with the AIR principle shown below. Although the AIR principle appear simple, it is very critical to the testing quality. Good unit testing is generally automatic, independent, and repeatable.
A: Automatic
I: Independent
R: Repeatable
Unit testing must also be automatically executed in non-interactive ways. Test cases are usually executed regularly, and the execution process must be fully automated to make sense. A test output that requires manual checks is not good unit testing. System.out is not allowed to be used for manual verification in unit testing. You can use assert instead.
Unit testing can be repeated and cannot be affected by the external environment.
Note: Unit testing is usually put into continuous integration, and unit testing is executed each time when the code is checked in. If unit testing has dependencies on the external environment, such as a network, service, or middleware, the continuous integration mechanism may become unavailable.
Positive Example: In order not to be affected by the external environment, the design of the code must change the SUT dependency to injection and use a dependency injection (DI) framework, such as spring, to inject a native memory or mock during testing.
Recommendation: The unit test code is written to comply with the BCDE principle shown below to ensure the delivery quality of the tested modules.
B is for border, which means boundary value testing, including the cyclic border, special values, special time, and data sequence.
C is for correct, which means the correct input that gives the expected results.
D is for design, which means that unit tests are written in combination with the design documents.
E is for error, which means with the mandatory input of error information, such as illegal data, abnormal processes, and unauthorized services, to get the expected results.
According to the relevant information on the Internet, the opinions are summarized below:
During integration testing, all environment configurations of the project should be added, and all service interfaces that the project depends on should be started as well. It often takes minutes (or even tens of minutes) to execute a test case. However, test cases implemented with Mock do not need to load project environment configurations or start other service interfaces. So the execution speed is often within a few seconds, greatly improving the execution speed of unit testing.
In practice, many people use integration testing instead of unit testing or confuse integration testing with unit testing. The differences between unit testing and integration testing are summarized below:
The object of unit testing is for the program unit to realize a specific function while the object of integration testing is the module in the outline design planning and the combination between modules.
The main method in unit testing is code-based white box testing, but the main method in integration testing is function-based black box testing.
Integration testing is later than unit testing.
Unit testing mainly tests logic, functions, parameter transfer, variable reference, error handling, requirements, and specific requirements in the design of programs in the modules. Integration testing mainly verifies each interface, the data transfer relationship between interfaces, and whether the combined modules can achieve the expected results.
Alibaba Cloud Community - October 23, 2024
Alibaba Clouder - April 19, 2021
Changyi - September 2, 2021
Alibaba Cloud Community - March 22, 2023
Nick Patrocky - August 26, 2022
Alibaba Clouder - May 20, 2020
MSE provides a fully managed registration and configuration center, and gateway and microservices governance capabilities.
Learn MoreAn enterprise-level continuous delivery tool.
Learn More