Detailed design and implementation of unit test

Detailed design and implementation of unit test

Unit test principle:

  1. The execution results are automatically given through A series of assertions without human judgment (Alibaba development manual stipulates that no output is allowed to judge by naked eyes) (A)
  2. Test cases cannot depend on each other and are independent (I)
  3. Unit tests can be executed repeatedly and cannot be affected by external environment, such as database, remote call, middleware and other external dependencies, which cannot affect the execution of test cases (R)

Based on the above principles, in the one-sided stage, we do not rely on the Spring container as much as possible, but mock external dependencies, so as to achieve faster testing.

Frame selection


Based on the above comparison, we can choose Alibaba's TestableMock or Mockito+Powermock. Take the second scheme as an example.

Test preparation

Naming conventions:
There are no special requirements for naming in Mockito and Powermock, but we'd better establish the same path of the tested class in the Test directory, in the main directory, and name it after the tested class + Test. This has a good shortcut key generation in idea. (the shortcut keys Ctrl + Shift + T or Test in Generate can quickly Generate the Test class of the corresponding directory under Test.)
Dependency:
Usually, we just need to introduce the spring boot starter test dependency in the spring project, which includes some common modules such as Junit, Spring Test, AssertJ, Hamcrest, Mockito, etc. Other items can be imported by themselves.

Test process

Writing a single test generally includes three parts
1. Given (Mock external dependency & prepare Fake data)
2. When (call the tested method)
3. Then (assertion execution result)

case analysis

@Service
public class  UserService {
    @Autowired
    private UserDao userDao;
    private UserMsg userMsg;
    
    public String saySomething() {
    
       String user = userDao.getUserName();
       String key = userMsg.getKey();
       return user + key;
    }
}
@RunWith(MockitoJUnitRunner)
public  class  UserServiceTest{
	
	@InjectMocks
	private UserService userService;
	@Mock private UserDao userDao;
	@Mock private UserMsg userMsg;
	
	public void saySomething(){
		//Given
		when(userDao.getUserName()).thenReturn("Xiao Ming");
		when(userMsg.getKey()).thenReturn("like to study");
		//When
		String message = userServic.saySomething()
		//Then	
		assertNotNull(message);//Assertion is not empty
		assertEquals(message,"Xiao Ming loves learning");//Assert equality
		verify(userDao).getUserName();//The validation method was executed
		verify(userMsg,times(1)).getKey();//Number of validation method executions
		//Verify execution sequence
		InOrder inOrder = Mockito.inOrder(userDao,userMsg);
		inOrder.verify(userDao).getUserName();
		inOrder.verify(userServic).getKey();
	}
}

matters needing attention

Limitations of Mocktio:
Cannot mock static methods
Cannot mock private method
Cannot mock final method
Cannot mock constructor
Solution:
PowerMock is developed based on Mockito, and its syntax rules are basically consistent with Mockito
You can use PowerMock to complete the Mock of private/static/final and construction methods.

PowerMock use

Relevant notes:
@The RunWith(PowerMockRunner.class) annotation indicates that test cases are run using PowerMockRunner
@The PrepareForTest({NodeScheduler.class, NodeService.class,}) solution is used to add all classes to be tested. Here are two classes to be tested.
Add dependency:
testImplementation("org.powermock:powermock-api-mockito2:2.0.9")
testImplementation("org.powermock:powermock-module-junit4:2.0.9")

Mock final

public class BookDao {
  
  public final boolean stored(Book book) {
    System.out.println("......confirm whether specified book is stored by BookDao......");
    return true;
  }
}
@RunWith(PowerMockRunner.class)
@PrepareForTest({BookService.class, BookDao.class})
public class BookServiceTestWitPowerMock {
  @Test
  public void isStored() {
    //mock final
    Book book = PowerMockito.mock(Book.class);
    BookDao bookDao = PowerMockito.mock(BookDao.class);
    PowerMockito.when(bookDao.stored(book)).thenReturn(false);
  }
}

Mock private

public class BookService {
 
  private boolean checkExist(String name) {
    System.out.println("---BookService checkExist---");
    throw new UnsupportedOperationException("UserService checkExist unsupported exception.");
  }
}
@RunWith(PowerMockRunner.class)
@PrepareForTest(BookService.class)
public class BookServiceTestWithPowerMock {
 
  @Test
  public void exist() throws Exception {
    
    //mock private
    BookService bookService = PowerMockito.spy(new BookService());
    PowerMockito.doReturn(true).when(bookService, "checkExist", arg);
}

Mock static

public class BookDao {

  public static void insert(Book book) {
    throw new UnsupportedOperationException("BookDao does not support insert() operation.");
  }
}
@RunWith(PowerMockRunner.class)
@PrepareForTest({BookService.class, BookDao.class})
public class BookServiceTestWithPowerMock {

  @Test
  public void count() {
    //Mock static
    PowerMockito.mockStatic(BookDao.class);
    PowerMockito.when(BookDao.count()).thenReturn(10);
    }
  }

Mock New

public class UserService{
	public void saveUser(String username, String password){
	User user = new User(username,password);
	user.insert();
}
@Test
public void saveUser() throw Exception{
	String username = "user1";
	String password = "aaa";
	//mock new
	User user = PowerMockito.mock(User.class);
	PowerMockito.wenNew(User.class).withArguments(username,passsword).thenReturn(user);
	.....	
}

Coverage

  1. Idea comes with

Coverage framework

JaCoCo

Configuration in gradle


Execute gradle test to find the report in the build \ reports \ Jacobo directory.

matters needing attention
When JaCoCo and PowermMock are used together, there will be a conflict because JaCoCo ignores the classes in the annotation @ PrepareForTest({}). The solution is to use JaCoCo's offline mode.

reference resources:

https://w3sun.com/828.html

Tags: Java unit testing mockito

Posted on Fri, 05 Nov 2021 20:54:19 -0400 by kof20012