Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring - Validator does not trigger in integration test

The problem

Actually, this is a kind of "follow-up" question. I have 2 integration tests running for a file upload, testing the corresponding controller. Unfortunately, after using the solution of the answer from my other question, the second integration test that should "fail" (because the extension of the file is "wrong" and therefore the FileContainerValidator should reject the file) does not fail! The Validator doesn't even get called.

How to solve the problem?


My code

FileUploadController

@Controller
public class FileUploadController {

    private final FileStorageService fileStorageService;

    FileContainerValidator fileContainerValidator;

    @Autowired
    public FileUploadController(FileStorageService FileStorageService) {
        this.fileStorageService = FileStorageService;
    }

    @Autowired
    public void setDefaultFileContainerValidator(FileContainerValidator validator) {
        this.fileContainerValidator = validator;
    }

    @GetMapping("/upload")
    public String showTestFileUploadForm(@ModelAttribute Mapping mapping, FileContainer fileContainer, Model model) {
        String path = "ERROR";
        try {
            path = new ClassPathResource("data.csv").getFile().getPath();
        } catch (IOException e) {
            e.printStackTrace();
        }

        model.addAttribute("shortenedFile", new TableConstructor(path, mapping.getContentDelimiter()));
        return "upload";
    }

    @PostMapping("/upload")
    public String uploadFile(Model model, @Valid FileContainer fileContainer, BindingResult result) {
        if (!result.hasErrors()) {
            System.out.println("Fetching file");
            fileStorageService.store(fileContainer);
            //TODO: SUCCESS
            model.addAttribute("success", true);
        }

        return "upload";
    }

    @InitBinder("fileContainer")
    protected void initBinderFileContainer(WebDataBinder binder) {
        binder.setValidator(fileContainerValidator);
    }
}

FileUploadIntegrationTest

@RunWith(SpringRunner.class)
@WebMvcTest(FileUploadController.class)
public class FileUploadIntegrationTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private FileStorageService fileStorageService;

    @MockBean
    private FileContainerValidator fileContainerValidator;

    private MockMultipartFile correctFile =
            new MockMultipartFile("file", "filename.xml", "text/plain", "some xml".getBytes());

    private MockMultipartFile fileWithWrongExtension =
            new MockMultipartFile("file", "filename.txt", "text/plain", "this is a wrong file!".getBytes());

    private String testDestination = "/tmp";

    //THIS ONE WORKS
    @Test
    public void uploadMultipartTestFile() throws Exception {
        when(fileContainerValidator.supports(any(Class.class))).thenReturn(true);
        mockMvc.perform(MockMvcRequestBuilders.fileUpload("/upload")
                .file(correctFile)
                .param("destination", "/tmp"))
                .andExpect(model().attribute("success", true));
    }

    //THIS ONE DOES NOT WORK
    @Test
    public void errorOnUploadFileWithWrongExtension() throws Exception {
      when(fileContainerValidator.supports(any(Class.class))).thenReturn(true);
        mockMvc.perform(MockMvcRequestBuilders.fileUpload("/upload")
                .file(fileWithWrongExtension)
                .param("destination", "/tmp"))
                .andExpect(model().attributeHasFieldErrors("fileContainer.file"));
    }
}

FileContainerValidator

@Component
public class FileContainerValidator implements Validator {
    public boolean supports(Class<?> clazz) {
        return FileContainer.class.equals(clazz);
    }

    @Override
    public void validate(Object obj, Errors errors) {
        FileContainer fileContainer = (FileContainer) obj;
        MultipartFile file = fileContainer.getFile();
        String destination = fileContainer.getDestination();
        if (file != null) {
            if (file.getSize() == 0) {
                errors.rejectValue("file", "missing.file", "The file must not be null or empty!");
            }
            if(file.getOriginalFilename().contains(".")) {
                String extension = file.getOriginalFilename().split("\\.")[1];
                if (!extension.equalsIgnoreCase("xml") && !extension.equalsIgnoreCase("csv")) {
                    errors.rejectValue("file", "extension.file", "The file had the wrong extension!");
                }
            } else {
                errors.rejectValue("file", "extension.file", "The file had the wrong extension!");
            }
        } else {
            errors.rejectValue("file", "missing.file", "The file must not be null or empty!");
        }
        if(destination != null && !destination.isEmpty()){
            if(!destination.matches("([a-zA-Z]:)?(\\/[a-zA-Z0-9_.-]+)+\\/?"))
                errors.rejectValue("destination", "wrong.destination", "The destination is given wrong");
        } else {
            errors.rejectValue("destination", "missing.destination", "The destination is missing");
        }
    }
}

Form ("upload.html")

<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring4-4.dtd">
<html xmlns:th="http://www.thymeleaf.org" lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head th:include="head :: head (pagename='Upload File')"></head>

<body>

<div th:replace="fragments/nav :: nav">&copy; Static</div>
<div class="panel panel-default" id="welcome-panel">
    <div class="panel-heading">
        <h2>Upload File</h2>
    </div>
    <div class="panel-body">
        <div class="alert alert-success" th:if="${success}">File successfully uploaded!</div>
        <form method="POST" action="#" th:action="@{/upload}" th:object="${fileContainer}"
              enctype="multipart/form-data">
            <h4>Upload files here:</h4>
            <label for="file" class="upload-drop-zone">
                Just drag and drop files <br> or click to upload.
                <input type="file" th:field="*{file}" id="file" style="display:none;"/>
            </label>
            <br><br>
            <label for="destination">Destination:</label>
            <input type="text" value="/tmp" id="destination" th:field="*{destination}"/>
            <br>
            <button class="btn btn-success convert-button btn-block" type="submit" name="upload" id="upload">Create
            </button>
            <div class="alert alert-danger" th:if="${#fields.hasErrors('file')}" th:errors="*{file}"></div>
            <div class="alert alert-danger" th:if="${#fields.hasErrors('destination')}"
                 th:errors="*{destination}"></div>
        </form>

    </div>
</div>
</div>

</body>

</html>
like image 822
manniL Avatar asked Oct 14 '25 10:10

manniL


1 Answers

You are no longer using your "real" FileContainerValidator.

@MockBean
private FileContainerValidator fileContainerValidator;

That asks Spring to create a fake version of your FileContainerValidator. So any real code inside FileContainerValidator will no longer be called.

If you use:

@SpyBean
private FileContainerValidator fileContainerValidator;

That does use the real implementation, but allows you to mock out the odd method. So your mocking of the fileContainerValidator.supports method should still work.

like image 55
David Lavender Avatar answered Oct 16 '25 22:10

David Lavender



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!