All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ratpack.test.embed.EmbeddedApp Maven / Gradle / Ivy

There is a newer version: 2.0.0-rc-1
Show newest version
/*
 * Copyright 2013 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
 *
 *    http://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 ratpack.test.embed;

import org.slf4j.LoggerFactory;
import ratpack.func.Action;
import ratpack.func.Function;
import ratpack.handling.Chain;
import ratpack.handling.Handler;
import ratpack.handling.Handlers;
import ratpack.launch.HandlerFactory;
import ratpack.launch.LaunchConfig;
import ratpack.launch.LaunchConfigBuilder;
import ratpack.server.RatpackServer;
import ratpack.test.ApplicationUnderTest;
import ratpack.test.embed.internal.LaunchConfigEmbeddedApp;
import ratpack.test.http.TestHttpClient;
import ratpack.test.http.TestHttpClients;

import java.net.URI;
import java.nio.file.Path;
import java.util.function.Consumer;

import static ratpack.util.ExceptionUtils.uncheck;

/**
 * An application created and used at runtime, useful for functionally testing subsets of functionality.
 * 

* This mechanism can be used for functionally testing isolated sections of an application, * or for testing general libraries that provide reusable functionality (e.g. Ratpack Guice modules). *

* Different implementations expose different API that can be used to define the actual application under test. *

* As embedded applications also implement {@link ratpack.test.ApplicationUnderTest}, they are suitable for use with clients accessing the app via HTTP. * Implementations must ensure that the application is up and receiving request when returning from {@link #getAddress()}. * Be sure to {@link #close()} the application after use to free resources. * * @see ratpack.test.embed.internal.LaunchConfigEmbeddedApp */ public interface EmbeddedApp extends ApplicationUnderTest, AutoCloseable { /** * Creates an embedded application by building a {@link LaunchConfig}. *

* The given {@link LaunchConfigBuilder} will be configured to not have base dir, and to use an ephemeral port. * * @param function a function that builds a launch config from a launch config builder * @return a newly created embedded application */ static EmbeddedApp fromLaunchConfigBuilder(Function function) { return new LaunchConfigEmbeddedApp() { @Override protected LaunchConfig createLaunchConfig() { return uncheck(() -> function.apply(LaunchConfigBuilder.noBaseDir().development(true).port(0))); } }; } /** * Creates an embedded application by building a {@link LaunchConfig} with the given base dir. *

* The given {@link LaunchConfigBuilder} will be configured to use an ephemeral port. * * @param baseDir the base dir for the embedded app * @param function a function that builds a launch config from a launch config builder * @return a newly created embedded application */ static EmbeddedApp fromLaunchConfigBuilder(Path baseDir, Function function) { return new LaunchConfigEmbeddedApp() { @Override protected LaunchConfig createLaunchConfig() { return uncheck(() -> function.apply(LaunchConfigBuilder.baseDir(baseDir).development(true).port(0))); } }; } /** * Creates an embedded application with a default launch config (no base dir, ephemeral port) and the given handler. *

* If you need to tweak the launch config, use {@link #fromLaunchConfigBuilder(Path, Function)}. * * @param handlerFactory a handler factory * @return a newly created embedded application */ static EmbeddedApp fromHandlerFactory(HandlerFactory handlerFactory) { return fromLaunchConfigBuilder(lcb -> lcb.build(handlerFactory::create)); } /** * Creates an embedded application with a default launch config (ephemeral port) and the given handler. *

* If you need to tweak the launch config, use {@link #fromLaunchConfigBuilder(Path, Function)}. * * @param baseDir the base dir for the embedded app * @param handlerFactory a handler factory * @return a newly created embedded application */ static EmbeddedApp fromHandlerFactory(Path baseDir, HandlerFactory handlerFactory) { return fromLaunchConfigBuilder(baseDir, lcb -> lcb.build(handlerFactory::create)); } /** * Creates an embedded application with a default launch config (no base dir, ephemeral port) and the given handler. *

* If you need to tweak the launch config, use {@link #fromLaunchConfigBuilder(Function)}. * * @param handler the application handler * @return a newly created embedded application */ static EmbeddedApp fromHandler(Handler handler) { return fromLaunchConfigBuilder(lcb -> lcb.build(lc -> handler)); } /** * Creates an embedded application with a default launch config (ephemeral port) and the given handler. *

* If you need to tweak the launch config, use {@link #fromLaunchConfigBuilder(Path, Function)}. * * @param baseDir the base dir for the embedded app * @param handler the application handler * @return a newly created embedded application */ static EmbeddedApp fromHandler(Path baseDir, Handler handler) { return fromLaunchConfigBuilder(baseDir, lcb -> lcb.build(lc -> handler)); } /** * Creates an embedded application with a default launch config (no base dir, ephemeral port) and the given handler chain. *

* If you need to tweak the launch config, use {@link #fromLaunchConfigBuilder(Function)}. * * @param action the handler chain definition * @return a newly created embedded application */ static EmbeddedApp fromChain(Action action) { return fromLaunchConfigBuilder(lcb -> lcb.build(lc -> Handlers.chain(lc, action))); } /** * Provides the given consumer with a {@link #getHttpClient() test http client} for this application, then closes this application. *

* The application will be closed regardless of whether the given consumer throws an exception. *

{@code
   *
   * import ratpack.test.embed.EmbeddedApp;
   *
   * public class Example {
   *   public static void main(String... args) {
   *     EmbeddedApp.fromHandler(ctx -> ctx.render("ok"))
   *       .test(httpClient -> {
   *         assert httpClient.get().getBody().getText().equals("ok");
   *       });
   *   }
   * }
   * }
* * @param consumer a consumer that tests this embedded application */ default void test(Consumer consumer) { try { consumer.accept(getHttpClient()); } finally { close(); } } /** * The server for the application. *

* Calling this method does not implicitly start the server. * * @return The server for the application */ RatpackServer getServer(); /** * Creates a new test HTTP client that tests this embedded application. * * @return a new test HTTP client that tests this embedded application */ default TestHttpClient getHttpClient() { return TestHttpClients.testHttpClient(this); } /** * {@inheritDoc} */ @Override default public URI getAddress() { RatpackServer server = getServer(); try { if (!server.isRunning()) { server.start(); } return new URI(server.getScheme(), null, server.getBindHost(), server.getBindPort(), "/", null, null); } catch (Exception e) { throw uncheck(e); } } /** * Stops the server returned by {@link #getServer()}. *

* Exceptions thrown by calling {@link RatpackServer#stop()} are suppressed and written to {@link System#err System.err}. */ @Override default public void close() { try { getServer().stop(); } catch (Exception e) { LoggerFactory.getLogger(this.getClass()).error("", e); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy