Class FlashTest

java.lang.Object
dev.relism.flash.testing.FlashTest
All Implemented Interfaces:
org.junit.jupiter.api.extension.AfterAllCallback, org.junit.jupiter.api.extension.AfterEachCallback, org.junit.jupiter.api.extension.BeforeAllCallback, org.junit.jupiter.api.extension.BeforeEachCallback, org.junit.jupiter.api.extension.Extension

public final class FlashTest extends Object implements org.junit.jupiter.api.extension.BeforeAllCallback, org.junit.jupiter.api.extension.AfterAllCallback, org.junit.jupiter.api.extension.BeforeEachCallback, org.junit.jupiter.api.extension.AfterEachCallback
Boots a real FlashApp on an OS-assigned port for the duration of a test class or a single test, and gives you a client pointed at it.

 class UserRoutesTest {

     @RegisterExtension
     static FlashTest app = FlashTest.of(new BlogApp())
             .mock(UserService.class, new InMemoryUserService());

     @Test
     void listsUsers() {
         app.get("/api/users")
            .expectStatus(200)
            .expectBodyContains("alice");
     }
 }
 

More than one server

Because this is an ordinary object in a field rather than an annotation, a test class can hold as many as it needs, and one can be wired from another with plain Java. Startup is lazy — reading baseUri() boots that server on the spot — so declaration order does the wiring, with no reliance on JUnit's extension ordering:

 @RegisterExtension static FlashTest auth = FlashTest.of(new FakeOidcApp());
 @RegisterExtension static FlashTest api  = FlashTest.of(new BlogApp(auth.baseUri()));
 

Scope

A static field boots once for the class; a non-static field boots a fresh app for every test. That is stock JUnit field semantics — the isolation switch is the keyword.

Replacing services

mock(java.lang.Class<T>, T) installs its replacements as the last extension, after everything the application and its extensions declare, so a fake always wins. Any object will do — a hand-written fake or a Mockito mock you created; this module depends on no mocking library.
See Also:
  • Method Summary

    Modifier and Type
    Method
    Description
    void
    afterAll(org.junit.jupiter.api.extension.ExtensionContext context)
     
    void
    afterEach(org.junit.jupiter.api.extension.ExtensionContext context)
     
    dev.relism.flash.extension.FlashApp
    app()
    The running app — escape hatch for assertions the harness does not cover.
    Base URI of the running server, e.g.
    void
    beforeAll(org.junit.jupiter.api.extension.ExtensionContext context)
     
    void
    beforeEach(org.junit.jupiter.api.extension.ExtensionContext context)
     
    The client the harness issues requests with.
    delete(String path)
    Sends DELETE path with no headers or body.
    get(String path)
    Sends GET path with no headers or body.
    mock(Class<T> type, T instance)
    Replaces the service bound to type with instance for this server.
    static FlashTest
    of(dev.relism.flash.extension.FlashApplication application)
    Creates a harness for application.
    int
    OS-assigned port of the running server.
    profile(Consumer<dev.relism.flash.extension.FlashConfiguration.FlashConfigurationBuilder> profile)
    Customises the FlashConfiguration this server runs with — timeouts, HTTP/2 switches, buffer sizes.
    Starts a request with headers or a body; the HTTP verb sends it.
    ws(String path)
    Opens a WebSocket to path on this server.

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Method Details

    • of

      public static FlashTest of(dev.relism.flash.extension.FlashApplication application)
      Creates a harness for application. Nothing is bound or started until first use.

      FlashApplication is a functional interface, so a lambda works as well as a named class: FlashTest.of(app -> app.get("/ping", (req, res) -> "pong")).

    • profile

      public FlashTest profile(Consumer<dev.relism.flash.extension.FlashConfiguration.FlashConfigurationBuilder> profile)
      Customises the FlashConfiguration this server runs with — timeouts, HTTP/2 switches, buffer sizes.

      The harness stamps host, port and the shutdown drain window after this runs, so a profile cannot break it. Setting listener(...) or tls(...) is rejected: the harness owns the single plaintext loopback listener it hands you a client for.

    • mock

      public <T> FlashTest mock(Class<T> type, T instance)
      Replaces the service bound to type with instance for this server.

      Wins over anything the application or its extensions declare, including services provided by an installed FlashExtension.

      ponytail: overrides are applied to the app's root context. A service declared inside a mount(...) scope's child context shadows the root and is therefore not reachable — add child-context targeting if that ever comes up.

    • baseUri

      public URI baseUri()
      Base URI of the running server, e.g. http://127.0.0.1:41307.
    • port

      public int port()
      OS-assigned port of the running server.
    • app

      public dev.relism.flash.extension.FlashApp app()
      The running app — escape hatch for assertions the harness does not cover.
    • client

      public HttpClient client()
      The client the harness issues requests with.
    • request

      public FlashRequest request()
      Starts a request with headers or a body; the HTTP verb sends it.
    • get

      public FlashResponse get(String path)
      Sends GET path with no headers or body.
    • delete

      public FlashResponse delete(String path)
      Sends DELETE path with no headers or body.
    • ws

      public FlashWebSocket ws(String path)
      Opens a WebSocket to path on this server. Close it when done — a try-with-resources block is the usual shape.
    • beforeAll

      public void beforeAll(org.junit.jupiter.api.extension.ExtensionContext context)
      Specified by:
      beforeAll in interface org.junit.jupiter.api.extension.BeforeAllCallback
    • afterAll

      public void afterAll(org.junit.jupiter.api.extension.ExtensionContext context)
      Specified by:
      afterAll in interface org.junit.jupiter.api.extension.AfterAllCallback
    • beforeEach

      public void beforeEach(org.junit.jupiter.api.extension.ExtensionContext context)
      Specified by:
      beforeEach in interface org.junit.jupiter.api.extension.BeforeEachCallback
    • afterEach

      public void afterEach(org.junit.jupiter.api.extension.ExtensionContext context)
      Specified by:
      afterEach in interface org.junit.jupiter.api.extension.AfterEachCallback