UI Unit Testing in Spring-based Projects: Child should have this element as a parent

I am trying to create a simple test. It looks like this:

@SpringBootTest(classes = Application.class)
@ExtendWith(TreeOnFailureExtension.class)
public class PaymentFlowTest extends SpringUIUnitTest {

	@Test
	@WithAnonymousUser
	public void firstTest() {
		navigate("buy", ProductSelectorView.class);
	}

}

the “/buy” page that i go to is a redirector, it checks if the user is signed in or not and redirects to the correct page. Something like this:

@Route(value = "/buy")
@RouteAlias(value = "/buy")
public class BuyRedirector extends Div implements HasUrlParameter<String> {
	protected void onAttach(AttachEvent attachEvent) {
		    super.onAttach(attachEvent);
	        if (!user.isAnonymous()){
                    getUI().ifPresent(ui -> ui.navigate(SubscriptionSelectorView.class));
                } else {
                    getUI().ifPresent(ui -> ui.navigate(ProductSelectorView.class));
            }
}

The test fails with following error:

java.lang.AssertionError: Child should have this element as a parent

Heres the tree as reported by TreeOnFailureExtension.class:

timestamp = 2024-05-28T23:22:18.798479, value = Test PaymentFlowTest::firstTest failed with the tree:
└── MockedUI
└── ProductSelectorView[@style=‘width:100%;height:100%’, @theme=‘spacing’]
└── VerticalLayout[@class=‘product-container’, @style=‘width:100%;height:100%’, @theme=‘padding spacing’]
├── VerticalLayout[@class=‘circle-1’, @style=‘width:100%’, @theme=‘padding spacing’]
├── VerticalLayout[@class=‘circle-2’, @style=‘width:100%’, @theme=‘padding spacing’]
├── Image[@alt=‘logo’, @class=‘logo’, @src=‘images/logo.svg’]
└── VerticalLayout[@class=‘product-wrapper’, @style=‘width:100%’, @theme=‘spacing’]
└── VerticalLayout[@style=‘width:100%’, @theme=‘padding spacing’]
├── Span[text=‘xxx’, @class=‘section-product-headline no-bottom-margin’]
├── RadioButtonGroup[value=‘null’, dataprovider=‘com.vaadin.flow.data.provider.ListDataProvider@273037b8’, @class=‘product-selector’]
│ ├── RadioButton[disabled=‘false’]
│ │ └── Label[@slot=‘label’]
│ │ └── VerticalLayout[@style=‘width:100%;height:100%’, @theme=‘padding spacing’]
│ │ ├── Paragraph[text=‘xxx’, @class=‘selector-headline’]
│ │ └── Paragraph[@class=‘description-text’]
│ │ └── Html[

xxx…]
│ └── RadioButton[disabled=‘false’]
│ └── Label[@slot=‘label’]
│ └── VerticalLayout[@style=‘width:100%;height:100%’, @theme=‘padding spacing’]
│ ├── Paragraph[text=‘xxx’, @class=‘selector-headline’]
│ └── Paragraph[@class=‘description-text’]
│ └── Html[

xxx…]
└── Button[caption=‘Næste’, @class=‘next-button’]

What should i do ?

You should not navigate in an onAttach hook. To perform a redirect, make your class implement BeforeEnterObserver and in the beforeEnter method use the event helpers to navigate (reroute or forward)

    @Override
    public void beforeEnter(BeforeEnterEvent event) {
        if (!user.isAnonymous()) {
            event.rerouteTo(SubscriptionSelectorView.class);
        } else {
            event.rerouteTo(ProductSelectorView.class);
        }
    }

Side note: the @RouteAlias annotations ca be removed as it uses the same path as @Route

1 Like

Thank you. That worked.
Curious, why shouldn’t i redirect in onAttach?

It looks like in a running application, there is no issue with calling navigate during onAttach. So, it may be some kind of bug in UI Unit testing.

Anyway, I think beforeEnter and its event helper methods are a better option for redirecting, since it integrates better in the navigation process.

Side note: in onAttach you can get the UI instance directly from the event, instead of using the getUI optional.

Funny you mention that. Even in beforeEnter, if i use navigate, it doesnt work.

It only started working after using the event.rerouteTo.