【问题标题】:Spring/Thymeleaf Unit test: test does not send value for model correctlySpring/Thymeleaf 单元测试:测试未正确发送模型的值
【发布时间】:2017-10-17 09:53:26
【问题描述】:

我正在为我的代码编写单元测试。现在我想测试输入表单的值是否正确保存在控制器的变量中。依赖此模型属性正确的两个测试均失败。因为模型存在但保持为空,这一定意味着我以错误的方式发送测试中的值。如何让我的测试包含输入的值以正确测试 post 方法?

测试 testPostValueInModel() 失败并出现 AssertionError:

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

我必须注意,我对这一切都很陌生,所以如果有人有答案,请提供更多代码示例并解释问题所在,以便我从错误中吸取教训。谢谢。

这是我的测试类:

@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("chosenTemp"));
        } 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("chosenTemp"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

控制器:

@Controller
public class InvoerschermController {

    private String chosenTemp = "20";
    private static PostgresDatabase database;
    private static Connection connection;

    // Static initializer for the database
    static {
        database = new PostgresDatabase();
        connection = database.connectToDatabase();
    }

    @GetMapping("/invoer")
    public String invoer(Model model) {
        // int newTemp = Integer.parseInt(getChosenTemp());
        chosenTemp = database.getTemperature(connection);
        model.addAttribute("chosenTemp", getChosenTemp());
        return "invoerscherm";
    }

    @PostMapping("/invoer")
    public String addInputTemp(String chosenTemp, Model model) {
        setChosenTemp(chosenTemp);
        model.addAttribute("chosenTemp", getChosenTemp());

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

        return "invoerscherm";
    }

    public String getChosenTemp() {
        return chosenTemp;
    }

    public void setChosenTemp(String chosenTemp) {
        this.chosenTemp = chosenTemp;
    }
}

百里香叶:

<!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>Temperatuur instellen</h1>
        </div>

        <form action="#" th:action="@{/invoer}" th:object="${invoerscherm}"
            method="post">
            <div class="form-group">
                <label for="chosenTemp">Gewenste temperatuur:</label> <input
                    type="text" class="form-control" id="chosenTemp" name="chosenTemp"
                    autocomplete="off" th:value="${chosenTemp}" />
            </div>

            <button type="submit" class="btn btn-default" name="submitKnop">Stel
                in</button>
        </form>
    </div>

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

【问题讨论】:

  • 您遇到异常还是只是测试失败了?你得到什么失败信息?
  • 您的代码有缺陷。永远不要将状态存储在单例中。您没有映射请求参数,因此没有什么要映射的。而且您不应该获取和缓存连接。您的 String chosenTemp 应该使用 @RequestParam("chosenTemp") 进行注释,并且您应该修复您的测试以传递该参数。
  • @72Services 我收到异常'java.lang.AssertionError:模型属性'chosenTemp'不存在'
  • 你为什么要问这个问题两次? stackoverflow.com/questions/46644515/…

标签: java spring unit-testing thymeleaf


【解决方案1】:

首先,您的控制器有缺陷。您不应该保留本地状态(尝试想象当 3 个用户同时提交时 chosenTemp 字段会发生什么情况,因为只有一个 InvoerschermController 实例。

您的方法参数应使用@RequestParam("chosenTemp") 注释以匹配您发送的表单。您的测试还应该反映您正在发送一个名为chosenTemp 的参数这一事实。

首先是你的控制器

@Controller
public class InvoerschermController {

    private static PostgresDatabase database;
    private static Connection connection;

    // Static initializer for the database
    static {
        database = new PostgresDatabase();
        connection = database.connectToDatabase();
    }

    @GetMapping("/invoer")
    public String invoer(Model model) {
        Integer chosenTemp = database.getTemperature(connection);
        model.addAttribute("chosenTemp", chosenTemp);
        return "invoerscherm";
    }

    @PostMapping("/invoer")
    public String addInputTemp(@RequestParam("chosenTemp") Integer chosenTemp, Model model) {
        model.addAttribute("chosenTemp", chosenTemp);
            database.setTemperature(connection, chosenTemp);
        return "invoerscherm";
    }
}

注意类型从String 更改为Integer Spring 将为您进行类型转换并注意添加了@RequestParam。现在您的测试也应该反映这一点。

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

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

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

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

注意添加了.param("chosenTemp", "20") 以添加具有该名称的请求参数。

恕我直言,您的控制器仍然存在缺陷,因为它不应该关心应该全部封装在您的 Database 类中的 Connection。尽管您的测试现在可能有效,但由于使用了 Thymeleaf 和表单绑定,您的实际应用程序仍然会失败。表单绑定期望键 invoerScherm 下的对象可用,并且该对象应具有名为 chosenTemp 的属性。您实际上缺少一个表单对象。那么你的控制器实际上应该是什么样子。

首先你需要一个表单对象:

public class InvoerScherm {
    private Integer chosenTemp;
    public InvoerScherm() {}
    public InvoerScherm(Integer temp) { this.chosenTemp=temp;}
    // Here be getters/setters
}

然后让你的控制器创建并使用它

@Controller
public class InvoerschermController {

    private static PostgresDatabase database;
    private static Connection connection;

    // Static initializer for the database
    static {
        database = new PostgresDatabase();
        connection = database.connectToDatabase();
    }

    @GetMapping("/invoer")
    public String invoer(Model model) {
        Integer chosenTemp = database.getTemperature(connection);
        InvoerScherm invoerScherm = new InvoerScherm(chosenTemp);
        model.addAttribute("invoerScherm", invoerScherm);
        return "invoerscherm";
    }

    @PostMapping("/invoer")
    public String addInputTemp(@ModelAttribute InvoerScherm invoerScherm, Model model) {
            database.setTemperature(connection, invoerScherm.getChosenTemp());
        return "invoerscherm";
    }
}

当然,现在您的测试将再次失败,但我将这项任务留给您。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-12-15
    • 1970-01-01
    • 1970-01-01
    • 2019-06-21
    • 2015-06-24
    • 2017-12-19
    • 2010-12-26
    相关资源
    最近更新 更多