Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Boot/Thymeleaf Unit Test: Model attribute does not exist

I have made a view with a form where the user can enter a value for inputTemp and the input is saved in an attribute in the Controller.

View:

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head th:include="fragments/template :: head"></head>
<head>
  <title>Smart CV</title>
</head>
<body>

<nav th:replace="fragments/template :: header"></nav>

<div class="container">
  <div class="hero-unit">
    <h1>Invoerscherm</h1>
  </div>
</div>

<form action="#" th:action="@{/invoer}" th:object="${invoerscherm}" method="post">
  <td><input type="text" id="inputTemp" name="inputTemp" th:value="${inputTemp}"/></td>
  <td><input name="submitKnop" type="submit" value="Input Temp"/></td>
</form>

<nav th:replace="fragments/template :: footer"></nav>
</body>
</html>

Controller:

@Controller
public class InvoerschermController {

    private String inputTemp = "20";

    @GetMapping("/invoer")
    public String invoer(Model model) {
        model.addAttribute("inputTemp", getInputTemp());
        System.out.println("1: " + model.toString());
        return "invoerscherm";
    }

    @PostMapping("/invoer")
    public String addInputTemp(String inputTemp, Model model) {
        setInputTemp(inputTemp);
        model.addAttribute("inputTemp", getInputTemp());
        System.out.println("2: " + model.toString());

        try {
            int newTemp = Integer.parseInt(getInputTemp());
            PostgresDatabase database = new PostgresDatabase();
            Connection connection = database.connectToDatabase();
            database.setTemperature(connection, newTemp);
        } catch (NumberFormatException nfe) {
            System.err.println("Invalid number: " + nfe.getMessage());
        }

        return "invoerscherm";
    }

    public String getInputTemp() {
        return inputTemp;
    }

    public void setInputTemp(String inputTemp) {
        this.inputTemp = inputTemp;
    }
}

Now, I want to write a UnitTest to check if the inputTemp is saved correctly. These are my unit tests for that:

@RunWith(SpringRunner.class)
@WebMvcTest(InvoerschermController.class)
@AutoConfigureMockMvc
public class InvoerschermTest {
    @Autowired
    private MockMvc mockMvc;    

    @Test
    public void testCorrectModel() {
        try {
            this.mockMvc.perform(get("/invoer", "20")).andExpect(status().isOk())
                    .andExpect(model().attributeExists("inputTemp"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Test
    public void testPost() {
        try {
            this.mockMvc.perform(post("/invoer", "20")).andExpect(status().isOk())
                    .andExpect(view().name("invoerscherm"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Test
    public void testPostValueInModel() {
        try {
            this.mockMvc.perform(post("/invoer", "20")).andExpect(status().isOk())
                    .andExpect(model().attributeExists("inputTemp"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Now two of my tests fail (testCorrectModel() and testPostValueInModel()), both with the following message:

java.lang.AssertionError: Model attribute 'inputTemp' does not exist

As far as I know, the attribute does exist, so somewhere I'm doing something wrong, and I just don't know where I'm going wrong. EDIT: it seems I am not sending the variable to the addInputTemp method correctly. How should I do this?

like image 286
Kailayla Avatar asked Nov 17 '25 06:11

Kailayla


1 Answers

in the functions testCorrectModel, testPostValueInModel the value of the model attribute is null since the private field value of inputTemp is null in the first case and in the second case the String inputTemp parameter is null.

Thus, in these tests functions you are actually making this call

model.addAttribute("inputTemp", null);

The size of the map is 1 because you have added an attribute in the map but the model().attributeExists would fail because the value of this attribute is null.

here is the internal implementation of attributeExists in spring framework

public ResultMatcher attributeExists(final String... names) {
        return new ResultMatcher() {
            public void match(MvcResult result) throws Exception {
                ModelAndView mav = getModelAndView(result);
                for (String name : names) {
                    assertTrue("Model attribute '" + name + "' does not exist", mav.getModel().get(name) != null);
                }
            }
        };
    }

Thus, set the value of the inputTemp correctly. For example for the @RequestMapping("/invoer") how do you set the value of inputTemp? In a different function? Maybe, should you pass it as a path variable?

like image 50
Periklis Douvitsas Avatar answered Nov 18 '25 19:11

Periklis Douvitsas