Browse Source
This commit adds AssertJ compatible assertions for the component that produces the result from the request. See gh-21178pull/32467/head
4 changed files with 416 additions and 0 deletions
@ -0,0 +1,62 @@
@@ -0,0 +1,62 @@
|
||||
/* |
||||
* Copyright 2002-2024 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.test.util; |
||||
|
||||
import java.lang.reflect.Method; |
||||
|
||||
import org.assertj.core.api.AbstractObjectAssert; |
||||
import org.assertj.core.api.Assertions; |
||||
|
||||
import org.springframework.lang.Nullable; |
||||
|
||||
/** |
||||
* AssertJ {@link org.assertj.core.api.Assert assertions} that can be applied |
||||
* to a {@link Method}. |
||||
* |
||||
* @author Stephane Nicoll |
||||
* @since 6.2 |
||||
*/ |
||||
public class MethodAssert extends AbstractObjectAssert<MethodAssert, Method> { |
||||
|
||||
public MethodAssert(@Nullable Method actual) { |
||||
super(actual, MethodAssert.class); |
||||
as("Method %s", actual); |
||||
} |
||||
|
||||
/** |
||||
* Verify that the actual method has the given {@linkplain Method#getName() |
||||
* name}. |
||||
* @param name the expected method name |
||||
*/ |
||||
public MethodAssert hasName(String name) { |
||||
isNotNull(); |
||||
Assertions.assertThat(this.actual.getName()).as("Method name").isEqualTo(name); |
||||
return this.myself; |
||||
} |
||||
|
||||
/** |
||||
* Verify that the actual method is declared in the given {@code type}. |
||||
* @param type the expected declaring class
|
||||
*/ |
||||
public MethodAssert hasDeclaringClass(Class<?> type) { |
||||
isNotNull(); |
||||
Assertions.assertThat(this.actual.getDeclaringClass()) |
||||
.as("Method declaring class").isEqualTo(type); |
||||
return this.myself; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,120 @@
@@ -0,0 +1,120 @@
|
||||
/* |
||||
* Copyright 2002-2024 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.test.web.servlet.assertj; |
||||
|
||||
import java.lang.reflect.Method; |
||||
|
||||
import org.assertj.core.api.AbstractObjectAssert; |
||||
import org.assertj.core.api.Assertions; |
||||
|
||||
import org.springframework.cglib.core.internal.Function; |
||||
import org.springframework.lang.Nullable; |
||||
import org.springframework.test.util.MethodAssert; |
||||
import org.springframework.util.ClassUtils; |
||||
import org.springframework.web.method.HandlerMethod; |
||||
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder; |
||||
import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder.MethodInvocationInfo; |
||||
|
||||
/** |
||||
* AssertJ {@link org.assertj.core.api.Assert assertions} that can be applied to |
||||
* a handler or handler method. |
||||
|
||||
* @author Stephane Nicoll |
||||
* @since 6.2 |
||||
*/ |
||||
public class HandlerResultAssert extends AbstractObjectAssert<HandlerResultAssert, Object> { |
||||
|
||||
public HandlerResultAssert(@Nullable Object actual) { |
||||
super(actual, HandlerResultAssert.class); |
||||
as("Handler result"); |
||||
} |
||||
|
||||
/** |
||||
* Return a new {@linkplain MethodAssert assertion} object that uses |
||||
* the {@link Method} that handles the request as the object to test. |
||||
* Verify first that the handler is a {@linkplain #isMethodHandler() method |
||||
* handler}. |
||||
* Example: <pre><code class='java'> |
||||
* // Check that a GET to "/greet" is invoked on a "handleGreet" method name
|
||||
* assertThat(mvc.perform(get("/greet")).handler().method().hasName("sayGreet"); |
||||
* </code></pre> |
||||
*/ |
||||
public MethodAssert method() { |
||||
return new MethodAssert(getHandlerMethod()); |
||||
} |
||||
|
||||
/** |
||||
* Verify that the handler is managed by a method invocation, typically on |
||||
* a controller. |
||||
*/ |
||||
public HandlerResultAssert isMethodHandler() { |
||||
return isNotNull().isInstanceOf(HandlerMethod.class); |
||||
} |
||||
|
||||
/** |
||||
* Verify that the handler is managed by the given {@code handlerMethod}. |
||||
* This creates a "mock" for the given {@code controllerType} and record the |
||||
* method invocation in the {@code handlerMethod}. The arguments used by the |
||||
* target method invocation can be {@code null} as the purpose of the mock |
||||
* is to identify the method that was invoked. |
||||
* Example: <pre><code class='java'> |
||||
* // If the method has a return type, you can return the result of the invocation
|
||||
* assertThat(mvc.perform(get("/greet")).handler().isInvokedOn( |
||||
* GreetController.class, controller -> controller.sayGreet()); |
||||
* // If the method has a void return type, the controller should be returned
|
||||
* assertThat(mvc.perform(post("/persons/")).handler().isInvokedOn( |
||||
* PersonController.class, controller -> controller.createPerson(null, null)); |
||||
* </code></pre> |
||||
* @param controllerType the controller to mock |
||||
* @param handlerMethod the method |
||||
*/ |
||||
public <T> HandlerResultAssert isInvokedOn(Class<T> controllerType, Function<T, Object> handlerMethod) { |
||||
MethodAssert actual = method(); |
||||
Object methodInvocationInfo = handlerMethod.apply(MvcUriComponentsBuilder.on(controllerType)); |
||||
Assertions.assertThat(methodInvocationInfo) |
||||
.as("Method invocation on controller '%s'", controllerType.getSimpleName()) |
||||
.isInstanceOfSatisfying(MethodInvocationInfo.class, mii -> |
||||
actual.isEqualTo(mii.getControllerMethod())); |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Verify that the handler is of the given {@code type}. For a controller |
||||
* method, this is the type of the controller. |
||||
* Example: <pre><code class='java'> |
||||
* // Check that a GET to "/greet" is managed by GreetController
|
||||
* assertThat(mvc.perform(get("/greet")).handler().hasType(GreetController.class); |
||||
* </code></pre> |
||||
* @param type the expected type of the handler |
||||
*/ |
||||
public HandlerResultAssert hasType(Class<?> type) { |
||||
isNotNull(); |
||||
Class<?> actualType = this.actual.getClass(); |
||||
if (this.actual instanceof HandlerMethod handlerMethod) { |
||||
actualType = handlerMethod.getBeanType(); |
||||
} |
||||
Assertions.assertThat(ClassUtils.getUserClass(actualType)).as("Handler result type").isEqualTo(type); |
||||
return this; |
||||
} |
||||
|
||||
private Method getHandlerMethod() { |
||||
isMethodHandler(); // validate type
|
||||
return ((HandlerMethod) this.actual).getMethod(); |
||||
} |
||||
|
||||
|
||||
} |
||||
@ -0,0 +1,92 @@
@@ -0,0 +1,92 @@
|
||||
/* |
||||
* Copyright 2002-2024 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.test.util; |
||||
|
||||
import java.lang.reflect.Method; |
||||
|
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.lang.Nullable; |
||||
import org.springframework.util.ReflectionUtils; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; |
||||
|
||||
/** |
||||
* Tests for {@link MethodAssert}. |
||||
* |
||||
* @author Stephane Nicoll |
||||
*/ |
||||
class MethodAssertTests { |
||||
|
||||
@Test |
||||
void isEqualTo() { |
||||
Method method = ReflectionUtils.findMethod(TestData.class, "counter"); |
||||
assertThat(method).isEqualTo(method); |
||||
} |
||||
|
||||
@Test |
||||
void hasName() { |
||||
assertThat(ReflectionUtils.findMethod(TestData.class, "counter")).hasName("counter"); |
||||
} |
||||
|
||||
@Test |
||||
void hasNameWithWrongName() { |
||||
Method method = ReflectionUtils.findMethod(TestData.class, "counter"); |
||||
assertThatExceptionOfType(AssertionError.class) |
||||
.isThrownBy(() -> assertThat(method).hasName("invalid")) |
||||
.withMessageContainingAll("Method name", "counter", "invalid"); |
||||
} |
||||
|
||||
@Test |
||||
void hasNameWithNullMethod() { |
||||
Method method = ReflectionUtils.findMethod(TestData.class, "notAMethod"); |
||||
assertThatExceptionOfType(AssertionError.class) |
||||
.isThrownBy(() -> assertThat(method).hasName("name")) |
||||
.withMessageContaining("Expecting actual not to be null"); |
||||
} |
||||
|
||||
@Test |
||||
void hasDeclaringClass() { |
||||
assertThat(ReflectionUtils.findMethod(TestData.class, "counter")).hasDeclaringClass(TestData.class); |
||||
} |
||||
|
||||
@Test |
||||
void haDeclaringClassWithWrongClass() { |
||||
Method method = ReflectionUtils.findMethod(TestData.class, "counter"); |
||||
assertThatExceptionOfType(AssertionError.class) |
||||
.isThrownBy(() -> assertThat(method).hasDeclaringClass(Method.class)) |
||||
.withMessageContainingAll("Method declaring class", |
||||
TestData.class.getCanonicalName(), Method.class.getCanonicalName()); |
||||
} |
||||
|
||||
@Test |
||||
void hasDeclaringClassWithNullMethod() { |
||||
Method method = ReflectionUtils.findMethod(TestData.class, "notAMethod"); |
||||
assertThatExceptionOfType(AssertionError.class) |
||||
.isThrownBy(() -> assertThat(method).hasDeclaringClass(TestData.class)) |
||||
.withMessageContaining("Expecting actual not to be null"); |
||||
} |
||||
|
||||
|
||||
private MethodAssert assertThat(@Nullable Method method) { |
||||
return new MethodAssert(method); |
||||
} |
||||
|
||||
|
||||
record TestData(String name, int counter) {} |
||||
|
||||
} |
||||
@ -0,0 +1,142 @@
@@ -0,0 +1,142 @@
|
||||
/* |
||||
* Copyright 2002-2024 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.test.web.servlet.assertj; |
||||
|
||||
import java.lang.reflect.Method; |
||||
|
||||
import org.assertj.core.api.AssertProvider; |
||||
import org.assertj.core.api.Assertions; |
||||
import org.junit.jupiter.api.Test; |
||||
|
||||
import org.springframework.http.ResponseEntity; |
||||
import org.springframework.util.ReflectionUtils; |
||||
import org.springframework.web.bind.annotation.GetMapping; |
||||
import org.springframework.web.bind.annotation.PostMapping; |
||||
import org.springframework.web.bind.annotation.RestController; |
||||
import org.springframework.web.method.HandlerMethod; |
||||
import org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType; |
||||
|
||||
/** |
||||
* Tests for {@link HandlerResultAssert}. |
||||
* |
||||
* @author Stephane Nicoll |
||||
*/ |
||||
class HandlerResultAssertTests { |
||||
|
||||
@Test |
||||
void hasTypeUseController() { |
||||
assertThat(handlerMethod(new TestController(), "greet")).hasType(TestController.class); |
||||
} |
||||
|
||||
@Test |
||||
void isMethodHandlerWithMethodHandler() { |
||||
assertThat(handlerMethod(new TestController(), "greet")).isMethodHandler(); |
||||
} |
||||
|
||||
@Test |
||||
void isMethodHandlerWithServletHandler() { |
||||
AssertProvider<HandlerResultAssert> actual = handler(new DefaultServletHttpRequestHandler()); |
||||
assertThatExceptionOfType(AssertionError.class) |
||||
.isThrownBy(() -> assertThat(actual).isMethodHandler()) |
||||
.withMessageContainingAll(DefaultServletHttpRequestHandler.class.getName(), |
||||
HandlerMethod.class.getName()); |
||||
} |
||||
|
||||
@Test |
||||
void methodName() { |
||||
assertThat(handlerMethod(new TestController(), "greet")).method().hasName("greet"); |
||||
} |
||||
|
||||
@Test |
||||
void declaringClass() { |
||||
assertThat(handlerMethod(new TestController(), "greet")).method().hasDeclaringClass(TestController.class); |
||||
} |
||||
|
||||
@Test |
||||
void method() { |
||||
assertThat(handlerMethod(new TestController(), "greet")).method().isEqualTo( |
||||
ReflectionUtils.findMethod(TestController.class, "greet")); |
||||
} |
||||
|
||||
@Test |
||||
void methodWithServletHandler() { |
||||
AssertProvider<HandlerResultAssert> actual = handler(new DefaultServletHttpRequestHandler()); |
||||
assertThatExceptionOfType(AssertionError.class) |
||||
.isThrownBy(() -> assertThat(actual).method()) |
||||
.withMessageContainingAll(DefaultServletHttpRequestHandler.class.getName(), |
||||
HandlerMethod.class.getName()); |
||||
} |
||||
|
||||
@Test |
||||
void isInvokedOn() { |
||||
assertThat(handlerMethod(new TestController(), "greet")) |
||||
.isInvokedOn(TestController.class, TestController::greet); |
||||
} |
||||
|
||||
@Test |
||||
void isInvokedOnWithVoidMethod() { |
||||
assertThat(handlerMethod(new TestController(), "update")) |
||||
.isInvokedOn(TestController.class, controller -> { |
||||
controller.update(); |
||||
return controller; |
||||
}); |
||||
} |
||||
|
||||
@Test |
||||
void isInvokedOnWithWrongMethod() { |
||||
AssertProvider<HandlerResultAssert> actual = handlerMethod(new TestController(), "update"); |
||||
assertThatExceptionOfType(AssertionError.class) |
||||
.isThrownBy(() -> assertThat(actual).isInvokedOn(TestController.class, TestController::greet)) |
||||
.withMessageContainingAll( |
||||
method(TestController.class, "greet").toGenericString(), |
||||
method(TestController.class, "update").toGenericString()); |
||||
} |
||||
|
||||
|
||||
private static AssertProvider<HandlerResultAssert> handler(Object instance) { |
||||
return () -> new HandlerResultAssert(instance); |
||||
} |
||||
|
||||
private static AssertProvider<HandlerResultAssert> handlerMethod(Object instance, String name, Class<?>... parameterTypes) { |
||||
HandlerMethod handlerMethod = new HandlerMethod(instance, method(instance.getClass(), name, parameterTypes)); |
||||
return () -> new HandlerResultAssert(handlerMethod); |
||||
} |
||||
|
||||
private static Method method(Class<?> target, String name, Class<?>... parameterTypes) { |
||||
Method method = ReflectionUtils.findMethod(target, name, parameterTypes); |
||||
Assertions.assertThat(method).isNotNull(); |
||||
return method; |
||||
} |
||||
|
||||
@RestController |
||||
public static class TestController { |
||||
|
||||
@GetMapping("/greet") |
||||
public ResponseEntity<String> greet() { |
||||
return ResponseEntity.ok().body("Hello"); |
||||
} |
||||
|
||||
@PostMapping("/update") |
||||
public void update() { |
||||
} |
||||
|
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue