mvysny / karibu-testing

Vaadin Server-Side Browserless Containerless Unit Testing
Apache License 2.0
105 stars 14 forks source link

Update Vaadin 24.1.9 breaks Karibu on MockRouteNotFoundError #158

Closed timomeinen closed 9 months ago

timomeinen commented 9 months ago

The latest change in Vaadin 24.1.9 introduced an addtional check in this commit:

com.vaadin.flow.router.internal.AbstractNavigationStateRenderer#handleTriggeredBeforeEvent

    if (beforeEvent.hasRerouteTarget()
            && (!isSameNavigationState(beforeEvent.getRerouteTargetType(),
                    beforeEvent.getRerouteTargetRouteParameters())
                    || !navigationState.getResolvedPath()
                            .equals(beforeEvent.getRerouteUrl()))) {
        return Optional.of(reroute(event, beforeEvent));
    }

When called with an instance of MockRouteNotFoundError the navigationState.getResolvedPath() is null and we get a NPE:

java.lang.NullPointerException: Cannot invoke "String.equals(Object)" because the return value of "com.vaadin.flow.router.NavigationState.getResolvedPath()" is null
    at com.vaadin.flow.router.internal.AbstractNavigationStateRenderer.handleTriggeredBeforeEvent(AbstractNavigationStateRenderer.java:672) ~[flow-server-24.1.11.jar:24.1.11]
    at com.vaadin.flow.router.internal.AbstractNavigationStateRenderer.sendBeforeEnterEvent(AbstractNavigationStateRenderer.java:602) ~[flow-server-24.1.11.jar:24.1.11]
    at com.vaadin.flow.router.internal.AbstractNavigationStateRenderer.sendBeforeEnterEvent(AbstractNavigationStateRenderer.java:577) ~[flow-server-24.1.11.jar:24.1.11]
    at com.vaadin.flow.router.internal.AbstractNavigationStateRenderer.createChainIfEmptyAndExecuteBeforeEnterNavigation(AbstractNavigationStateRenderer.java:453) ~[flow-server-24.1.11.jar:24.1.11]
    at com.vaadin.flow.router.internal.AbstractNavigationStateRenderer.handle(AbstractNavigationStateRenderer.java:210) ~[flow-server-24.1.11.jar:24.1.11]
    at com.vaadin.flow.router.internal.ErrorStateRenderer.handle(ErrorStateRenderer.java:106) ~[flow-server-24.1.11.jar:24.1.11]
    at com.vaadin.flow.router.internal.AbstractNavigationStateRenderer.reroute(AbstractNavigationStateRenderer.java:715) ~[flow-server-24.1.11.jar:24.1.11]
    at com.vaadin.flow.router.internal.AbstractNavigationStateRenderer.handleTriggeredBeforeEvent(AbstractNavigationStateRenderer.java:673) ~[flow-server-24.1.11.jar:24.1.11]
    at com.vaadin.flow.component.internal.JavaScriptNavigationStateRenderer.handleTriggeredBeforeEvent(JavaScriptNavigationStateRenderer.java:99) ~[flow-server-24.1.11.jar:24.1.11]
    at com.vaadin.flow.router.internal.AbstractNavigationStateRenderer.sendBeforeEnterEvent(AbstractNavigationStateRenderer.java:602) ~[flow-server-24.1.11.jar:24.1.11]
    at com.vaadin.flow.router.internal.AbstractNavigationStateRenderer.sendBeforeEnterEvent(AbstractNavigationStateRenderer.java:577) ~[flow-server-24.1.11.jar:24.1.11]
    at com.vaadin.flow.router.internal.AbstractNavigationStateRenderer.createChainIfEmptyAndExecuteBeforeEnterNavigation(AbstractNavigationStateRenderer.java:453) ~[flow-server-24.1.11.jar:24.1.11]
    at com.vaadin.flow.router.internal.AbstractNavigationStateRenderer.handle(AbstractNavigationStateRenderer.java:210) ~[flow-server-24.1.11.jar:24.1.11]
    at com.vaadin.flow.component.internal.JavaScriptNavigationStateRenderer.handle(JavaScriptNavigationStateRenderer.java:78) ~[flow-server-24.1.11.jar:24.1.11]
    at com.vaadin.flow.component.UI.handleNavigation(UI.java:1857) ~[flow-server-24.1.11.jar:24.1.11]
    at com.vaadin.flow.component.UI.navigate(UI.java:1246) ~[flow-server-24.1.11.jar:24.1.11]
    at com.vaadin.flow.component.UI.navigate(UI.java:1201) ~[flow-server-24.1.11.jar:24.1.11]
    at com.vaadin.flow.component.UI.navigate(UI.java:1004) ~[flow-server-24.1.11.jar:24.1.11]
    at com.vaadin.flow.component.UI.navigate(UI.java:897) ~[flow-server-24.1.11.jar:24.1.11]

My test expects a com.vaadin.flow.router.NotFoundException in order to check if the access was correctly prohibited. I tried to workaround, but I do not fully understand the concept of the MockRouteNotFoundError vs. NotFoundException.

When I remove the MockRouteNotFoundError from the routes the NPE does not appear, but the test does not work neither.

Any advice? Thanks.

timomeinen commented 9 months ago

You can see the same problem in the example project if updating to 24.1.9 in this test:

https://github.com/mvysny/vaadin-spring-karibu-testing

com.example.application.views.about.AboutViewTest#userCantSeeThis

java.lang.NullPointerException: Cannot invoke "String.equals(Object)" because the return value of "com.vaadin.flow.router.NavigationState.getResolvedPath()" is null
    at com.vaadin.flow.router.internal.AbstractNavigationStateRenderer.handleTriggeredBeforeEvent(AbstractNavigationStateRenderer.java:672) ~[flow-server-24.1.11.jar:24.1.11]
    at com.vaadin.flow.router.internal.AbstractNavigationStateRenderer.sendBeforeEnterEvent(AbstractNavigationStateRenderer.java:602) ~[flow-server-24.1.11.jar:24.1.11]
    at com.vaadin.flow.router.internal.AbstractNavigationStateRenderer.sendBeforeEnterEvent(AbstractNavigationStateRenderer.java:577) ~[flow-server-24.1.11.jar:24.1.11]
    at com.vaadin.flow.router.internal.AbstractNavigationStateRenderer.createChainIfEmptyAndExecuteBeforeEnterNavigation(AbstractNavigationStateRenderer.java:453) ~[flow-server-24.1.11.jar:24.1.11]
    at com.vaadin.flow.router.internal.AbstractNavigationStateRenderer.handle(AbstractNavigationStateRenderer.java:210) ~[flow-server-24.1.11.jar:24.1.11]
    at com.vaadin.flow.router.internal.ErrorStateRenderer.handle(ErrorStateRenderer.java:106) ~[flow-server-24.1.11.jar:24.1.11]
    at com.vaadin.flow.router.internal.AbstractNavigationStateRenderer.reroute(AbstractNavigationStateRenderer.java:715) ~[flow-server-24.1.11.jar:24.1.11]
    at com.vaadin.flow.router.internal.AbstractNavigationStateRenderer.handleTriggeredBeforeEvent(AbstractNavigationStateRenderer.java:673) ~[flow-server-24.1.11.jar:24.1.11]
    at com.vaadin.flow.component.internal.JavaScriptNavigationStateRenderer.handleTriggeredBeforeEvent(JavaScriptNavigationStateRenderer.java:99) ~[flow-server-24.1.11.jar:24.1.11]
    at com.vaadin.flow.router.internal.AbstractNavigationStateRenderer.sendBeforeEnterEvent(AbstractNavigationStateRenderer.java:602) ~[flow-server-24.1.11.jar:24.1.11]
    at com.vaadin.flow.router.internal.AbstractNavigationStateRenderer.sendBeforeEnterEvent(AbstractNavigationStateRenderer.java:577) ~[flow-server-24.1.11.jar:24.1.11]
    at com.vaadin.flow.router.internal.AbstractNavigationStateRenderer.createChainIfEmptyAndExecuteBeforeEnterNavigation(AbstractNavigationStateRenderer.java:453) ~[flow-server-24.1.11.jar:24.1.11]
    at com.vaadin.flow.router.internal.AbstractNavigationStateRenderer.handle(AbstractNavigationStateRenderer.java:210) ~[flow-server-24.1.11.jar:24.1.11]
    at com.vaadin.flow.component.internal.JavaScriptNavigationStateRenderer.handle(JavaScriptNavigationStateRenderer.java:78) ~[flow-server-24.1.11.jar:24.1.11]
    at com.vaadin.flow.component.UI.handleNavigation(UI.java:1857) ~[flow-server-24.1.11.jar:24.1.11]
    at com.vaadin.flow.component.UI.navigate(UI.java:1246) ~[flow-server-24.1.11.jar:24.1.11]
    at com.vaadin.flow.component.UI.navigate(UI.java:1201) ~[flow-server-24.1.11.jar:24.1.11]
    at com.vaadin.flow.component.UI.navigate(UI.java:1004) ~[flow-server-24.1.11.jar:24.1.11]
    at com.vaadin.flow.component.UI.navigate(UI.java:897) ~[flow-server-24.1.11.jar:24.1.11]
    at com.example.application.views.about.AboutViewTest.lambda$userCantSeeThis$0(AboutViewTest.java:30) ~[test-classes/:na]
    at org.junit.jupiter.api.AssertThrows.assertThrows(AssertThrows.java:53) ~[junit-jupiter-api-5.9.3.jar:5.9.3]
    at org.junit.jupiter.api.AssertThrows.assertThrows(AssertThrows.java:35) ~[junit-jupiter-api-5.9.3.jar:5.9.3]
    at org.junit.jupiter.api.Assertions.assertThrows(Assertions.java:3083) ~[junit-jupiter-api-5.9.3.jar:5.9.3]
    at com.example.application.views.about.AboutViewTest.userCantSeeThis(AboutViewTest.java:29) ~[test-classes/:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
    at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:727) ~[junit-platform-commons-1.9.3.jar:1.9.3]
    at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60) ~[junit-jupiter-engine-5.9.3.jar:5.9.3]
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131) ~[junit-jupiter-engine-5.9.3.jar:5.9.3]
    at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:156) ~[junit-jupiter-engine-5.9.3.jar:5.9.3]
    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:147) ~[junit-jupiter-engine-5.9.3.jar:5.9.3]
    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:86) ~[junit-jupiter-engine-5.9.3.jar:5.9.3]
    at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:103) ~[junit-jupiter-engine-5.9.3.jar:5.9.3]
    at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:93) ~[junit-jupiter-engine-5.9.3.jar:5.9.3]
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106) ~[junit-jupiter-engine-5.9.3.jar:5.9.3]
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64) ~[junit-jupiter-engine-5.9.3.jar:5.9.3]
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45) ~[junit-jupiter-engine-5.9.3.jar:5.9.3]
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37) ~[junit-jupiter-engine-5.9.3.jar:5.9.3]
    at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:92) ~[junit-jupiter-engine-5.9.3.jar:5.9.3]
    at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:86) ~[junit-jupiter-engine-5.9.3.jar:5.9.3]
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:217) ~[junit-jupiter-engine-5.9.3.jar:5.9.3]
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.9.3.jar:1.9.3]
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:213) ~[junit-jupiter-engine-5.9.3.jar:5.9.3]
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:138) ~[junit-jupiter-engine-5.9.3.jar:5.9.3]
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68) ~[junit-jupiter-engine-5.9.3.jar:5.9.3]
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151) ~[junit-platform-engine-1.9.3.jar:1.9.3]
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.9.3.jar:1.9.3]
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) ~[junit-platform-engine-1.9.3.jar:1.9.3]
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) ~[junit-platform-engine-1.9.3.jar:1.9.3]
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) ~[junit-platform-engine-1.9.3.jar:1.9.3]
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.9.3.jar:1.9.3]
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) ~[junit-platform-engine-1.9.3.jar:1.9.3]
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) ~[junit-platform-engine-1.9.3.jar:1.9.3]
    at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.compute(ForkJoinPoolHierarchicalTestExecutorService.java:202) ~[junit-platform-engine-1.9.3.jar:1.9.3]
    at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService.executeNonConcurrentTasks(ForkJoinPoolHierarchicalTestExecutorService.java:172) ~[junit-platform-engine-1.9.3.jar:1.9.3]
    at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService.invokeAll(ForkJoinPoolHierarchicalTestExecutorService.java:152) ~[junit-platform-engine-1.9.3.jar:1.9.3]
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) ~[junit-platform-engine-1.9.3.jar:1.9.3]
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.9.3.jar:1.9.3]
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) ~[junit-platform-engine-1.9.3.jar:1.9.3]
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) ~[junit-platform-engine-1.9.3.jar:1.9.3]
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) ~[junit-platform-engine-1.9.3.jar:1.9.3]
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.9.3.jar:1.9.3]
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) ~[junit-platform-engine-1.9.3.jar:1.9.3]
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) ~[junit-platform-engine-1.9.3.jar:1.9.3]
    at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.compute(ForkJoinPoolHierarchicalTestExecutorService.java:202) ~[junit-platform-engine-1.9.3.jar:1.9.3]
    at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService.executeNonConcurrentTasks(ForkJoinPoolHierarchicalTestExecutorService.java:172) ~[junit-platform-engine-1.9.3.jar:1.9.3]
    at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService.invokeAll(ForkJoinPoolHierarchicalTestExecutorService.java:152) ~[junit-platform-engine-1.9.3.jar:1.9.3]
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) ~[junit-platform-engine-1.9.3.jar:1.9.3]
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.9.3.jar:1.9.3]
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) ~[junit-platform-engine-1.9.3.jar:1.9.3]
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) ~[junit-platform-engine-1.9.3.jar:1.9.3]
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) ~[junit-platform-engine-1.9.3.jar:1.9.3]
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) ~[junit-platform-engine-1.9.3.jar:1.9.3]
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) ~[junit-platform-engine-1.9.3.jar:1.9.3]
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) ~[junit-platform-engine-1.9.3.jar:1.9.3]
    at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.compute(ForkJoinPoolHierarchicalTestExecutorService.java:202) ~[junit-platform-engine-1.9.3.jar:1.9.3]
    at java.base/java.util.concurrent.RecursiveAction.exec(RecursiveAction.java:194) ~[na:na]
    at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:373) ~[na:na]
    at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1182) ~[na:na]
    at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1655) ~[na:na]
    at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1622) ~[na:na]
    at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:165) ~[na:na]
mvysny commented 9 months ago

Yup, thank you for letting me know. This looks like a bug in Vaadin, I’ve filed https://github.com/vaadin/flow/issues/17597 . Looks like the best approach is to wait for next Vaadin to be released.

mvysny commented 9 months ago

On "MockRouteNotFoundError vs. NotFoundException vs RouteNotFoundError":

The RouteNotFoundError is just a page which is shown when the browser navigates to a non-existing route. That's usually what we want, but for tests it's better to throw an exception eagerly instead. That's what MockRouteNotFoundError does - when Vaadin cannot find the target route and tries to open MockRouteNotFoundError, the MockRouteNotFoundError class throws an exception in its constructor, which then bubbles out and makes the test fail.

Let me document this in the README file: https://github.com/mvysny/karibu-testing/tree/master/karibu-testing-v10#mockroutenotfounderror-vs-routenotfounderror

timomeinen commented 9 months ago

Thanks for the explanation. That is exactly what I want to achieve in my tests. Got it.

mvysny commented 9 months ago

@timomeinen please upgrade to Vaadin 24.1.10, the issue has been fixed there.

timomeinen commented 9 months ago

@mvysny Thank you. Tested and works as expected. That was a quickfix...