如何测试Lambda代码?AgentListView集成测试覆盖率问题排查
问题描述
我有一个包含两个方法的AgentListView类:
public class AgentListView extends StandardListView<Agent> { private transient UiComponents uiComponents; private transient AgentService agentService; private transient Notifications notifications; private transient Messages messages; public AgentListView(UiComponents uiComponents, AgentService agentService, Notifications notifications, Messages messages) { this.uiComponents = uiComponents; this.agentService = agentService; this.notifications = notifications; this.messages = messages; } @ViewComponent private DataGrid<Agent> agentsDataGrid; @Subscribe public void onInit(final InitEvent event) { addDropdownButtonInColumn(); } public void addDropdownButtonInColumn() { Grid.Column<Agent> column = agentsDataGrid.getColumnByKey("additionally"); DropdownButton dropdownButton = uiComponents.create(DropdownButton.class); dropdownButton.setIcon(VaadinIcon.ELLIPSIS_DOTS_V.create()); dropdownButton.addItem("edit", agentsDataGrid.getAction("edit")); dropdownButton.addItem("checkStatusAgent", agentsDataGrid.getAction("checkStatusAgent")); dropdownButton.addItem("read", agentsDataGrid.getAction("read")); ComponentRenderer<DropdownButton, Agent> componentRenderer = new ComponentRenderer<>(agent -> dropdownButton); column.setRenderer(componentRenderer); } }
我在AgentListView的集成测试类中编写了如下测试来验证onInit()方法:
@Test void testOnInit() { viewNavigators.view(AgentListView.class).navigate(); AgentListView agentListView = UiTestUtils.getCurrentView(); agentListView.onInit(mock(View.InitEvent.class)); }
但Jacoco标记出以下代码行中的dropdownButton未被覆盖:
ComponentRenderer<DropdownButton, Agent> componentRenderer = new ComponentRenderer<>(agent -> dropdownButton);
我知道这是Lambda表达式里的代码没被调用,但不知道怎么触发它来让测试完全覆盖,请问问题出在哪?
解决思路
问题根源
你创建的ComponentRenderer里的Lambda表达式agent -> dropdownButton,只有当Vaadin的DataGrid渲染行时才会被调用。当前测试仅初始化视图、调用onInit方法,并未触发DataGrid的渲染逻辑,因此Lambda内的代码不会执行,Jacoco会标记为未覆盖。
另外还有个隐藏bug:当前代码会让所有行复用同一个DropdownButton实例,这在Vaadin中是不允许的,每行必须有独立的组件实例,否则会引发UI异常。
修复步骤
先修复代码逻辑bug
修改Lambda,让每行渲染时都创建新的DropdownButton实例:ComponentRenderer<DropdownButton, Agent> componentRenderer = new ComponentRenderer<>(agent -> { DropdownButton dropdownButton = uiComponents.create(DropdownButton.class); dropdownButton.setIcon(VaadinIcon.ELLIPSIS_DOTS_V.create()); dropdownButton.addItem("edit", agentsDataGrid.getAction("edit")); dropdownButton.addItem("checkStatusAgent", agentsDataGrid.getAction("checkStatusAgent")); dropdownButton.addItem("read", agentsDataGrid.getAction("read")); return dropdownButton; });让测试触发Lambda执行
在测试中手动调用渲染器的createComponent方法,模拟DataGrid的行渲染动作:@Test void testOnInit() { viewNavigators.view(AgentListView.class).navigate(); AgentListView agentListView = UiTestUtils.getCurrentView(); agentListView.onInit(mock(View.InitEvent.class)); // 获取目标列的渲染器 Grid.Column<Agent> column = agentListView.agentsDataGrid.getColumnByKey("additionally"); ComponentRenderer<?, Agent> renderer = (ComponentRenderer<?, Agent>) column.getRenderer(); // 创建测试用Agent实例,触发Lambda执行 Agent testAgent = new Agent(); renderer.createComponent(testAgent); }
如果agentsDataGrid是私有成员,可选择:
- 将其改为包访问权限,或添加包级别的getter方法
- 用反射获取该字段(不推荐,仅在无法修改原代码时使用)
补充说明
如果是集成测试,也可以通过往DataGrid添加数据、触发UI渲染的方式让Lambda执行,但手动调用渲染器的方法更直接高效,适合单元/集成测试场景。
内容的提问来源于stack exchange,提问作者SanyaKirilloff
相关产品推荐
相关产品推荐

