I have a problem with understanding why the logic mocked inside @BeforeAll works, but only for the first test. They work fine separately, copying the identical logic to both tests will produce the same result – 1 passed, 1 failed. What happens: in AboutUsService.update() on the first line appears the mistake at
pageRepository.getByName(ABOUT_US_PAGE).orElseThrow(null);
For the first test, the calling of the method produces the expected result and returns AboutUsPage with its data. But for the next one it produces Optional.empty and NullPointerException eventually. What is the trick, why mocking works only for one test? Also, I checked out if the real repository is getting called, but it seems like something else caused it. Changing the annotation to @BeforeEach solves this problem, but shouldn’t it work as well with @BeforeAll? Spring Boot v. 2.3.4
@SpringBootTest(classes = {AboutUsService.class, AboutUsPageRepository.class, AboutUsPageImageStore.class, ImageUtil.class}) @TestInstance(Lifecycle.PER_CLASS) public class AboutUsServiceTest { @Autowired private AboutUsService aboutUsService; @MockBean private AboutUsPageRepository pageRepository; @MockBean private AboutUsPageImageStore aboutUsPageImageStore; @MockBean private ImageUtil imageUtil; @BeforeAll public void configure() { when(pageRepository.getByName(anyString())) .thenReturn(Optional.of(AboutUsTestData.getAboutUsPage())); when(aboutUsPageImageStore.setContent(any(AboutUsPageImage.class), any(Resource.class))) .thenReturn(new AboutUsPageImage()); } @Test public void updateWithImagesTest() { when(imageUtil.fileIsValid(any(MultipartFile.class))) .thenReturn(true); MultipartFile validFile = new MockMultipartFile("name", "etc", "content", "Hello".getBytes()); aboutUsService.update(new MultipartFile[]{validFile}); verify(pageRepository, times(1)).save(any(AboutUsPage.class)); } @Test public void updateWithNoImagesTest() { AboutUsPage updated = aboutUsService.update(new MultipartFile[0]); AboutUsPage expected = AboutUsTestData.getAboutUsPage(); assertSame(expected.getImages().size(), updated.getImages().size()); } } @Service @AllArgsConstructor @Slf4j public class AboutUsService { public static final String ABOUT_US_PAGE = "ABOUT_US_PAGE"; private final AboutUsPageImageStore pageImageStore; private final ImageUtil imageUtil; private final AboutUsPageRepository pageRepository; @Transactional public AboutUsPage update(MultipartFile[] images) { AboutUsPage page = pageRepository.getByName(ABOUT_US_PAGE).orElseThrow(null); if (ArrayUtils.isNotEmpty(images)) { List<AboutUsPageImage> pageImages = new ArrayList<>(); Arrays.stream(images).forEach(extraImage -> { if (imageUtil.fileIsValid(extraImage)) { AboutUsPageImage infoImage = new AboutUsPageImage(); pageImageStore.setContent(infoImage, extraImage.getResource()); infoImage.setPage(page); pageImages.add(infoImage); } }); if (CollectionUtils.isNotEmpty(pageImages)) { page.setImages(pageImages); } return pageRepository.save(page); } else { return page; } } } @Repository public interface AboutUsPageRepository extends JpaRepository<AboutUsPage, Long> { Optional<AboutUsPage> getByName(String name); }
Advertisement
Answer
@BeforeAll
is executed once, before any test is executed. If you need logic that is executed before every test, use @Before