001/*
002 * Copyright 2002-2019 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.test.web.servlet.result;
018
019import java.lang.reflect.Method;
020
021import org.hamcrest.Matcher;
022
023import org.springframework.test.web.servlet.MvcResult;
024import org.springframework.test.web.servlet.ResultMatcher;
025import org.springframework.util.ClassUtils;
026import org.springframework.web.method.HandlerMethod;
027import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder;
028import org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder.MethodInvocationInfo;
029import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
030import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
031
032import static org.hamcrest.MatcherAssert.assertThat;
033import static org.springframework.test.util.AssertionErrors.assertEquals;
034import static org.springframework.test.util.AssertionErrors.assertNotNull;
035import static org.springframework.test.util.AssertionErrors.assertTrue;
036import static org.springframework.test.util.AssertionErrors.fail;
037
038/**
039 * Factory for assertions on the selected handler or handler method.
040 *
041 * <p>An instance of this class is typically accessed via
042 * {@link MockMvcResultMatchers#handler}.
043 *
044 * <p><strong>Note:</strong> Expectations that assert the controller method
045 * used to process the request work only for requests processed with
046 * {@link RequestMappingHandlerMapping} and {@link RequestMappingHandlerAdapter}
047 * which is used by default with the Spring MVC Java config and XML namespace.
048 *
049 * @author Rossen Stoyanchev
050 * @author Sam Brannen
051 * @since 3.2
052 */
053public class HandlerResultMatchers {
054
055        /**
056         * Protected constructor.
057         * Use {@link MockMvcResultMatchers#handler()}.
058         */
059        protected HandlerResultMatchers() {
060        }
061
062
063        /**
064         * Assert the type of the handler that processed the request.
065         */
066        public ResultMatcher handlerType(Class<?> type) {
067                return result -> {
068                        Object handler = result.getHandler();
069                        assertNotNull("No handler", handler);
070                        if (handler != null) {
071                                Class<?> actual = handler.getClass();
072                                if (HandlerMethod.class.isInstance(handler)) {
073                                        actual = ((HandlerMethod) handler).getBeanType();
074                                }
075                                assertEquals("Handler type", type, ClassUtils.getUserClass(actual));
076                        }
077                };
078        }
079
080        /**
081         * Assert the controller method used to process the request.
082         * <p>The expected method is specified through a "mock" controller method
083         * invocation similar to {@link MvcUriComponentsBuilder#fromMethodCall(Object)}.
084         * <p>For example, given this controller:
085         * <pre class="code">
086         * &#064;RestController
087         * public class SimpleController {
088         *
089         *     &#064;RequestMapping("/")
090         *     public ResponseEntity&lt;Void&gt; handle() {
091         *         return ResponseEntity.ok().build();
092         *     }
093         * }
094         * </pre>
095         * <p>A test that has statically imported {@link MvcUriComponentsBuilder#on}
096         * can be performed as follows:
097         * <pre class="code">
098         * mockMvc.perform(get("/"))
099         *     .andExpect(handler().methodCall(on(SimpleController.class).handle()));
100         * </pre>
101         * @param obj either the value returned from a "mock" controller invocation
102         * or the "mock" controller itself after an invocation
103         */
104        public ResultMatcher methodCall(Object obj) {
105                return result -> {
106                        if (!(obj instanceof MethodInvocationInfo)) {
107                                fail(String.format("The supplied object [%s] is not an instance of %s. " +
108                                                "Ensure that you invoke the handler method via MvcUriComponentsBuilder.on().",
109                                                obj, MethodInvocationInfo.class.getName()));
110                        }
111                        MethodInvocationInfo invocationInfo = (MethodInvocationInfo) obj;
112                        Method expected = invocationInfo.getControllerMethod();
113                        Method actual = getHandlerMethod(result).getMethod();
114                        assertEquals("Handler method", expected, actual);
115                };
116        }
117
118        /**
119         * Assert the name of the controller method used to process the request
120         * using the given Hamcrest {@link Matcher}.
121         */
122        public ResultMatcher methodName(Matcher<? super String> matcher) {
123                return result -> {
124                        HandlerMethod handlerMethod = getHandlerMethod(result);
125                        assertThat("Handler method", handlerMethod.getMethod().getName(), matcher);
126                };
127        }
128
129        /**
130         * Assert the name of the controller method used to process the request.
131         */
132        public ResultMatcher methodName(String name) {
133                return result -> {
134                        HandlerMethod handlerMethod = getHandlerMethod(result);
135                        assertEquals("Handler method", name, handlerMethod.getMethod().getName());
136                };
137        }
138
139        /**
140         * Assert the controller method used to process the request.
141         */
142        public ResultMatcher method(Method method) {
143                return result -> {
144                        HandlerMethod handlerMethod = getHandlerMethod(result);
145                        assertEquals("Handler method", method, handlerMethod.getMethod());
146                };
147        }
148
149
150        private static HandlerMethod getHandlerMethod(MvcResult result) {
151                Object handler = result.getHandler();
152                assertTrue("Not a HandlerMethod: " + handler, handler instanceof HandlerMethod);
153                return (HandlerMethod) handler;
154        }
155
156}