This is the first day of my participation in the August Challenge. For details, see:August is more challenging

In the old code to write unit tests for the company recently, found in the code there is a lot of Domain layer code USES SpringContextUtil. The getBean () this method, access to the Bean Sping, this led to a common unit test of writing can’t access to the object, triggered a null pointer exception, The unit test failed, after some exploration, found that using PowerMockito can be a perfect solution to the problem.

Business Code Example

Verify that a field has the same properties as a field in the database

private void check(String code, String name) {
  if (SpringContextUtils.getBean(FieldRepo.class).checkFieldExist(code, name, getDepartmentId()) > 0) {
      throw new BusinessException("Field name or code duplicate"); }}Copy the code

Unit Test Example

The @PrepareForTest(SpringContextutils.class) annotation should be added to the class name of the unit test

Writing a

@Rule
public ExpectedException thrown = ExpectedException.none();

@Test
public void createCheckAndProcessTest1(a) {
  PowerMockito.mockStatic(SpringContextUtils.class);
  PowerMockito.when(SpringContextUtils.getBean(FieldRepo.class)).thenReturn(fieldRepo());
  PowerMockito.when(fieldRepo.checkFieldExist(Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(1);
  FieldBO fieldBO = new FieldBO();
  thrown.expect(BusinessException.class);
  thrown.expectMessage("Field name or code duplicate");
  fieldBO.createCheckAndProcess();
}


public FieldRepo fieldRepo(a) {
  return new FieldRepo(){
 
@Override
public int countList(FieldPageInfoBO param) {
  return 0;
}

@Override
public List<FieldBO> queryList(FieldPageInfoBO param) {
  return null;
}

@Override
public int checkFieldExist(String code, String name, Long departmentId) {
  return 0; }... }}Copy the code

One of the disadvantages of this approach is that the Mock FieldRepo object is created as a new object, and since the object is an interface class, once the interface changes, the unit test methods must also be modified. Otherwise, the project will compile incorrectly and the unit test will fail. Is not the best way to write, after the author groped, found another more beautiful way to write.

Write two

@Mock FieldRepo fieldRepo; @Rule public ExpectedException thrown = ExpectedException.none(); @Test public void createCheckAndProcessTest1() { PowerMockito.mockStatic(SpringContextUtils.class); PowerMockito.when(SpringContextUtils.getBean(FieldRepo.class)).thenReturn(fieldRepo); PowerMockito.when(fieldRepo.checkFieldExist(Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(1); FieldBO fieldBO = new FieldBO(); thrown.expect(BusinessException.class); Thrown. ExpectMessage (" field name or code duplication "); fieldBO.createCheckAndProcess(); }Copy the code

Mock the FieldRepo object directly using the @Mock annotation to decouple it from the actual implementation of the interface methods, so that no matter how FieldRepo changes, the unit tests will not be affected, and the Mock implements the default return for FieldRepo through the proxy.