001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.rest.mock;
014
015import java.util.*;
016import java.util.concurrent.*;
017
018import org.apache.juneau.rest.*;
019import org.apache.juneau.utils.*;
020
021/**
022 * Creates a mocked interface against a REST resource class.
023 *
024 * <p>
025 * Allows you to test your REST resource classes without a running servlet container.
026 *
027 * <h5 class='figure'>Example:</h5>
028 * <p class='bcode w800'>
029 *  <jk>public class</jk> MockTest {
030 *
031 *    <jc>// Our REST resource to test.</jc>
032 *    <ja>@RestResource</ja>(serializers=JsonSerializer.Simple.<jk>class</jk>, parsers=JsonParser.<jk>class</jk>)
033 *    <jk>public static class</jk> MyRest {
034 *
035 *       <ja>@RestMethod</ja>(name=<jsf>PUT</jsf>, path=<js>"/String"</js>)
036 *       <jk>public</jk> String echo(<ja>@Body</ja> String b) {
037 *          <jk>return</jk> b;
038 *       }
039 *    }
040 *
041 *  <ja>@Test</ja>
042 *  <jk>public void</jk> testEcho() <jk>throws</jk> Exception {
043 *    MockRest
044 *       .<jsf>create</jsf>(MyRest.<jk>class</jk>)
045 *       .put(<js>"/String"</js>, <js>"'foo'"</js>)
046 *       .execute()
047 *       .assertStatus(200)
048 *       .assertBody(<js>"'foo'"</js>);
049 *  }
050 * </p>
051 *
052 * <h5 class='section'>See Also:</h5>
053 * <ul>
054 *    <li class='link'>{@doc juneau-rest-server.UnitTesting}
055 *    <li class='link'>{@doc juneau-rest-client.UnitTesting}
056 * </ul>
057 */
058public class MockRest implements MockHttpConnection {
059   private static Map<Class<?>,RestContext> CONTEXTS = new ConcurrentHashMap<>();
060
061   private final RestContext rc;
062
063   private MockRest(Class<?> c, boolean debug) throws Exception {
064      if (! CONTEXTS.containsKey(c)) {
065         Object r = c.newInstance();
066         RestContext rc = RestContext.create(r).logger(debug ? BasicRestLogger.class : NoOpRestLogger.class).build();
067         if (r instanceof RestServlet) {
068            ((RestServlet)r).setContext(rc);
069         } else {
070            rc.postInit();
071         }
072         rc.postInitChildFirst();
073         CONTEXTS.put(c, rc);
074      }
075      rc = CONTEXTS.get(c);
076   }
077
078   /**
079    * Create a new mock REST interface
080    *
081    * @param c The REST class.
082    * @return A new mock interface.
083    * @throws RuntimeException
084    *    For testing conveniences, this method wraps all exceptions in a RuntimeException so that you can easily define mocks as reusable fields.
085    */
086   public static MockRest create(Class<?> c) throws RuntimeException {
087      return create(c, false);
088   }
089
090   /**
091    * Create a new mock REST interface
092    *
093    * @param c The REST class.
094    * @param debug
095    *    If <jk>true</jk>, the REST interface will use the {@link BasicRestLogger} for logging.
096    *    <br>Otherwise, uses {@link NoOpRestLogger}.
097    * @return A new mock interface.
098    * @throws RuntimeException
099    *    For testing conveniences, this method wraps all exceptions in a RuntimeException so that you can easily define mocks as reusable fields.
100    */
101   public static MockRest create(Class<?> c, boolean debug) throws RuntimeException {
102      try {
103         return new MockRest(c, debug);
104      } catch (Exception e) {
105         throw new RuntimeException(e);
106      }
107   }
108
109   /**
110    * Performs a REST request against the REST interface.
111    *
112    * @param method The HTTP method
113    * @param path The URI path.
114    * @param body The body of the request.
115    * @return A new servlet request.
116    * @throws Exception
117    */
118   @Override /* MockHttpConnection */
119   public MockServletRequest request(String method, String path, Object body) throws Exception {
120      return MockServletRequest.create(method, path).body(body).restContext(rc);
121   }
122
123   /**
124    * Performs a REST request against the REST interface.
125    *
126    * @param method The HTTP method
127    * @param path The URI path.
128    * @return A new servlet request.
129    * @throws Exception
130    */
131   public MockServletRequest request(String method, String path) throws Exception {
132      return request(method, path, null);
133   }
134
135   /**
136    * Perform a GET request.
137    *
138    * @param path The URI path.
139    * @return A new servlet request.
140    * @throws Exception
141    */
142   public MockServletRequest get(String path) throws Exception {
143      return request("GET", path, null);
144   }
145
146   /**
147    * Perform a PUT request.
148    *
149    * @param path The URI path.
150    * @param body The body of the request.
151    * @return A new servlet request.
152    * @throws Exception
153    */
154   public MockServletRequest put(String path, Object body) throws Exception {
155      return request("PUT", path, body);
156   }
157
158   /**
159    * Perform a POST request.
160    *
161    * @param path The URI path.
162    * @param body The body of the request.
163    * @return A new servlet request.
164    * @throws Exception
165    */
166   public MockServletRequest post(String path, Object body) throws Exception {
167      return request("POST", path, body);
168   }
169
170   /**
171    * Perform a DELETE request.
172    *
173    * @param path The URI path.
174    * @return A new servlet request.
175    * @throws Exception
176    */
177   public MockServletRequest delete(String path) throws Exception {
178      return request("DELETE", path, null);
179   }
180
181   /**
182    * Perform an OPTIONS request.
183    *
184    * @param path The URI path.
185    * @return A new servlet request.
186    * @throws Exception
187    */
188   public MockServletRequest options(String path) throws Exception {
189      return request("OPTIONS", path, null);
190   }
191}