Docs

Documentation versions (currently viewingVaadin 24)
Documentation translations (currently viewingChinese)

此页面是从官方文档(http://vaadin.com/docs)进行机器翻译的。翻译内容可能存在错误、不准确或误述。Vaadin不对翻译的准确性、可靠性或及时性做任何保证或陈述。

UI单元测试中的组件查询

在UI单元测试中访问组件的细节和示例。

UIUnitTest 基类能够获取已实例化的视图,但子组件并不总是可以直接访问。例如,组件可能被保存在私有可见性的字段中,甚至可能根本没有在视图类中引用。

为了克服这种限制,UIUnitTest 提供了组件查询功能,让您能够在组件树中搜索并找到测试方法需要交互的组件。

组件查询(Component Queries)

您可以通过调用 $() 方法来获取一个 ComponentQuery 对象,并指定要搜索的组件类型。

当此查询对象已准备好,配置好所有条件后,使用终端操作符获得找到的组件。例如终端操作符包括 first()last()atIndex()all()id()

Source code
Java
// 获取UI中第一个TextField
TextField nameField = $(TextField.class).first();

限定查询范围(Scoping Queries)

通过使用 $view() 方法,您还可以将搜索范围限制在当前视图的子组件,或者使用 $(MyComponent.class, rootComponent) 将搜索范围限制在另一个组件中。

Source code
Java
// 获取当前视图中的第一个TextField
TextField nameField = $view(TextField.class).first();

// 获取一个容器中包含的第一个TextField
TextField nameField = $(TextField.class, view.formLayout).first();

查询对象提供许多过滤工具,可以细化搜索。比如,您可以依据组件的 id、属性值或使用自定义的谓词(predicate)条件对潜在候选进行过滤。

Source code
Java
// 获取带有指定标签的TextField
TextField nameField = $view(TextField.class)
        .withPropertyValue(TextField::getLabel, "First name")
        .single();

// 获取视图中符合条件的所有TextField
Predicate<TextField> fieldHasNotValue = field -> field.getOptionalValue().isEmpty();
Predicate<TextField> fieldIsInvalid = TextField::isInvalid;
List<TextField> textField = $view(TextField.class)
        .withCondition(fieldHasNotValue.or(fieldIsInvalid))
        .all();

有时您可能需要查询嵌套在UI中,并由不同类型组件组成的多个层级中的组件。为了简化这种情况,查询对象提供了方法,可以在找到的组件基础上链接新的查询,这样就可以以流式的方式创建复杂的查询。例如,thenOn() 方法及其变体,如 thenOnFirst(),会为您提供一个新的查询对象,并将搜索范围设置为当前查询所选中的组件。

Source code
链式查询示例
// 查找视图中的所有 'VerticalLayout' 组件
TextField textField = $view(VerticalLayout.class)
        // 选择第二个并开始搜寻其中的 'TextField'
        .thenOn(2, TextField.class)
        // 筛选禁用的 'TextField'
        .withCondition(tf -> !tf.isEnabled())
        // 最后获取满足条件的最后一个
        .last();

自定义测试程序(Custom Testers)

针对特定组件,可以使用自定义测试程序(custom testers),以提供针对该组件或其扩展组件的测试API。使用 @Tests 注解标记测试程序,并指定此测试程序适用的组件类型。

使用 test(Component.class, component) 获取一个通用测试程序,将会检查所有可用测试程序,确定是否有能 Tests 此组件或其超类型的测试器。

默认情况下,框架会扫描 com.vaadin.flow.component 包下的测试器实现,因此,如果您将自定义测试程序添加到这些包内并继承了 ComponentTester,则它立即可用。

如果您的自定义测试程序位于其他包中,则必须使用 @ComponentTesterPackages 在测试类中指定需要扫描的包位置。

Source code
定义自定义测试程序包
@ComponentTesterPackages("com.example.application.views.personform")
class PersonFormViewTest extends UIUnitTest {
}

自定义测试程序类可以内部调用其他测试程序,以下示例中演示了 PhoneNumberFieldTester 类的使用:

Source code
用于 CustomField 的示例自定义测试程序
// Tests注解指定此测试程序适用的组件类型
@Tests(PersonFormView.PhoneNumberField.class)
public class PhoneNumberFieldTester extends ComponentTester<PersonFormView.PhoneNumberField> {
    // 可在自定义测试程序内部使用其他测试程序
    final ComboBoxTester<ComboBox<String>, String> combo_;
    final TextFieldTester<TextField, String> number_;

    public PhoneNumberFieldWrap(PersonFormView.PhoneNumberField component) {
        super(component);
        combo_ = new ComboBoxTester<>(
                getComponent().countryCode);
        number_ = new TextFieldTester<>(getComponent().number);
    }

    public List<String> getCountryCodes() {
        return combo_.getSuggestionItems();
    }

    public void setCountryCode(String code) {
        ensureComponentIsUsable();
        if(!getCountryCodes().contains(code)) {
            throw new IllegalArgumentException("Given code isn't available for selection");
        }
        combo_.selectItem(code);
    }

    public void setNumber(String number) {
        ensureComponentIsUsable();
        number_.setValue(number);
    }

    public String getValue() {
        return getComponent().generateModelValue();
    }

}
Source code
PhoneNumberField.java
static class PhoneNumberField extends CustomField<String> {
    ComboBox<String> countryCode = new ComboBox<>();
    TextField number = new TextField();

    // ...
}

DDC7D136-1A56-44FC-B256-C15DB7645EDC