Searching for Components
The UIUnitTest
base class is able to get the instantiated view, but child components may not be always accessible directly.
For example, components may be stored in fields with private visibility or they may even not be referenced at all in the view class.
To overcome this limitation, UIUnitTest
provides a Component query functionality that lets you search the component tree for the components you need to interact with in test methods.
The ComponentQuery
object can be obtained by calling the $()
method, specifying the type of the component you are searching for.
You can also restrict search scope to the children of the current view by using the $view()
method, or even to another component by using $(MyComponent.class, rootComponent)
.
The query object has many filtering utilities that can be used to refine the search.
For example, you can filter by component id
, by a property value, or using custom predicates on potential candidates.
Once the query is ready with all the required conditions configured, you can execute it and then use terminal operators to retrieve the components that it found.
Examples of terminal operators are first()
, last()
, atIndex()
, all()
, and id()
.
// Get the first TextField in the UI
TextField nameField = $(TextField.class).first();
// Get the first TextField in the current view
TextField nameField = $view(TextField.class).first();
// Get the first TextField nested in a container
TextField nameField = $view(TextField.class, view.formLayout).first();
// Get the TextField with the given label
TextField nameField = $view(TextField.class)
.withPropertyValue(TextField::getLabel, "First name")
.single();
// Get all TextFields in the view that satisfies the conditions
Predicate<TextField> fieldHasNotValue = field -> field.getOptionalValue().isEmpty();
Predicate<TextField> fieldIsInvalid = TextField::isInvalid;
List<TextField> textField = $view(TextField.class)
.withCondition(fieldHasNotValue.or(fieldIsInvalid))
.all();
You may sometimes need to do a query for components nested inside the UI, in a hierarchy composed of many different types of components.
To simplify such situations, the query object offers methods to chain a new query starting with a found component, so that a complex query can be created in a fluent way.
The thenOn()
method and its variants, for example thenOnFirst()
, provide you with a new query object for the given component type, setting the search scope to the component selected from the current query.
// Search for all 'VerticalLayout's in the view
TextField textField = $view(VerticalLayout.class)
// take the second one and start searching for 'TextField's
.thenOn(2, TextField.class)
// filter for disabled 'TextField's
.withCondition(tf -> !tf.isEnabled())
// and get the last one
.last();
Custom Testers
There are custom testers for components that give a testing API for the component or one extending it.
Testers are annotated using the @Tests
annotation, which specifies which components the tester is for.
Getting a generic tester using test(Component.class, component)
will check the available testers to determine whether one exists that Tests
the component or its supertype.
By default, tester implementations are scanned from the com.vaadin.flow.component
package, so adding a custom tester to the package that extends ComponentTester
will make it immediately available.
To have the custom testers in another package, the test needs to be annotated with @ComponentTesterPackages
, containing the packages to scan for testers.
@ComponentTesterPackages("com.example.application.views.personform")
class PersonFormViewTest extends UIUnitTest {
}
Custom tester classes can use other testers internally, as demonstrated in PhoneNumberFieldTester
.
// Tests defines the components this tester should be used for automatically
@Tests(PersonFormView.PhoneNumberField.class)
public class PhoneNumberFieldTester extends ComponentTester<PersonFormView.PhoneNumberField> {
// Other testers can be used inside the custom tester
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 is not available for selection");
}
combo_.selectItem(code);
}
public void setNumber(String number) {
ensureComponentIsUsable();
number_.setValue(number);
}
public String getValue() {
return getComponent().generateModelValue();
}
}
static class PhoneNumberField extends CustomField<String> {
ComboBox<String> countryCode = new ComboBox<>();
TextField number = new TextField();
// ...
}
DDC7D136-1A56-44FC-B256-C15DB7645EDC