Tạo object sử dụng @Mock và @InjectMocks

Em đang làm JUnit test với Spring boot
Em đang vướng ở chỗ làm sao để một “Mock” vừa là một “InjectMock” được vì em đang gặp trường hợp như thế này:

  • loginApi có một dependency là userService
  • nhưng userService cũng có một dependency là userRepository
    nếu dùng @Mock thì userRepository bị null còn nếu dùng @InjectMocks thì chính userService bị null
    mong mọi ng chỉ giáo ạ
    image
1 Like

Hi there,

Cho tớ hỏi chút:

  1. Cậu đang test gì vậy? Test loginApi hay userService?
    Nếu test loginApi, và cậu đã mock UserService, thì cậu không cần quan tâm bên trong UserService có gì đâu. Tương tự với userService không cần quan tâm loginApi có gì đâu.
  2. Trong TH cậu test loginApi, cậu có sử dụng trực tiếp userRepository của userService không?
    Nếu có, cậu nên bỏ nó đi. Nó vừa làm cậu khó test, vừa là bad practice, khi loginApi phải biết userService có userRepository. Cậu biết encapsulation phải không?

Mong nhận được câu trả lời từ cậu.

5 Likes

không cậu ơi loginApi không trực tiếp sử dụng userRepository. Ban đầu mình cũng nghĩ là mock userService thì ko cần quan tâm dependency của nó nhưng khi chạy thì lại ko đc. Debug mới thấy là hàm của userService không chạy đúng vì userRepository null. userService của mình đây. Trong loginApi mình dùng hàm userService.findByUsername()

@Service
public class UserService implements IUserService {

	@Autowired
	private UserRepository userRepository;

	@Override
	public UserEntity findByUsername(String username) {
		UserEntity foundEntity = null;
		try {
			foundEntity = userRepository.findByUsername(username);
		} catch (Exception e) {
			return null;
		}
		return foundEntity;
	}
}
2 Likes

Tớ hiểu.
Tớ đoán là cậu test loginApi. Nếu sai thì cậu sửa lại cho tớ nhé, vì tớ không nhận được câu trả lời nào cho câu hỏi đó.

Khi mock (làm giả) đối tượng UserService, nếu loginApi cần sử dụng UserService#findByUsername, thì cậu phải mock method UserService#findByUsername.
Tớ đoán là cậu chưa làm điều đó.
Nếu không, dễ hiểu khi nó gọi tới hàm thật.

Giờ việc của cậu là tìm cách mock method UserService#findByUsername là được. Cậu có thể dễ dàng google điều này, vì đó là điều cơ bản nhất khi cậu sử dụng 1 thư viện mock.

5 Likes

Mình test loginApi, xl cậu.
Cậu cho mình hỏi thêm 1 chút nhé. Mình mới xem cái mock method mà không hiểu tại sao lại bắt hàm trả về 1 obj rồi sau đó lại verify luôn bằng cái obj đó. Thì test ở đây để làm gì mình không hiểu lắm. Mong bạn giải đáp giúp mình. Mình xem cái vd này

@Test
public void test1()  {
        //  create mock
        MyClass test = mock(MyClass.class);

        // define return value for method getUniqueId()
        when(test.getUniqueId()).thenReturn(43);

        // use mock in test....
        assertEquals(test.getUniqueId(), 43);
}
3 Likes

Ừ cậu, trong test case mà cậu viết, nó không có ích gì thật :smiley: Có chăng. chỉ tăng chỉ số code coverage thôi.

Rule of thumb ở đây là cậu chỉ nên test những hàm có chứa logic. Trong trường hợp của cậu, getter chỉ đơn thuần trả về giá tri, không làm bất cứ thứ gì fancy, nên cậu không cần test nó.
Một trường hợp khác cậu không cần test, là các method chỉ có nhiệm vụ forward cho đối tượng khác - ví dụ UserService#fetchUser, method này chỉ đơn giản forward việc khó nhất cho UserRepository và đưa kết quả cho layer khác, mà không có bất cứ xử lý nào.

Ví dụ về hàm mà cậu nên test: Giả sử cậu cần test UserService#calculateAge method - hàm này lấy dữ liệu năm sinh từ database và tính ra tuổi.
Lúc đó, cậu cần mock UserRepository#selectUser(int) để fake dữ liệu người dùng nào đó từ DB, sau đó verify kết quả tính toán từ dữ liệu fake đó.

Hi vọng ví dụ trên giúp cậu hiểu được :smiley:

6 Likes

Ok mình hiểu r cảm ơn cậu nhé

2 Likes
83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?