简体   繁体   中英

Why values ​are overwritten when stubbing in Junit5?

I'm using Junit5 for test my spring code.

In the test code, I mock SetRepository, to make the getSize() method return value 40. Then I stubbed ListService's findSetSize() method and made it return value 30.

Then print the getSize() method value. Expected 40, but 30 printed.

It looks likegetSize() was overwritten while stubbing, but I'd like to know why.

// ListService.java
@Service
@RequiredArgsConstructor
public class ListService {

    private final SetRepository setRepository;

    public int findSetSize() {
        int size = setRepository.getSize();
        System.out.println(size);  // first result: 40, second result: 30
        return size;
    }

    public void saveToSet(int num) {
        setRepository.saveNum(num);
    }

    public void saveBigDummyData() {
        for(int i=0; i<10; i++) {
            setRepository.saveNum(i);
        }
    }
}

// SetRepository.java
@Component
public class SetRepository {

    Set<Integer> set = new HashSet<>();

    public int getSize() {
        return set.size();
    }

    public void saveNum(int num) {
        set.add(num);
    }
}

// Test code for ListService
@ExtendWith(MockitoExtension.class)
public class ListServiceWithMockTest {

    @InjectMocks
    ListService subject;


    @Mock
    SetRepository setRepository;

    @Test
    @DisplayName("can get Set size")
    public void findSetSizeTest() {
        //given
        when(setRepository.getSize()).thenReturn(40);
        when(subject.findSetSize()).thenReturn(30);

        //when
        subject.saveToSet(100);

        //then
        System.out.println(setRepository.getSize());  // expected:40, result: 30
        assertEquals(30, subject.findSetSize());
    }

}

Here is my code. I know I shouldn't do stubbing like this, but I'm curious why the value comes out like this. Thank U!!

In your example, you want to mock a method within the same class. But this is not possible in the way you did. You create a real object using @InjectMocks . You need to use Mockito.spy() to partially mock this.

If you want to get the results you expect correctly and really mock your findSetSize() method, you should change your test method to follow:

@Test
@DisplayName("can get Set size")
void findSetSizeTest() {
    //given
    ListService spyService = spy(subject);
    when(setRepository.getSize()).thenReturn(40);
    doReturn(30).when(spyService).findSetSize();

    //when
    subject.saveToSet(100);

    //then
    System.out.println(setRepository.getSize());  // expected:40, result: 30
    Assertions.assertEquals(30, spyService.findSetSize());
}

Here you have to understand that mocking would work only when you call it on the instance of spy object. It's wrapped by a Mockito proxy which catches your call, and if you have overriden some method, it will call your new implementation instead of the original one.

Also the documentation related to spy says:

You can create spies of real objects. When you use the spy then the real methods are called (unless a method was stubbed).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM