001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.microservice.test;
018
019import static java.lang.annotation.ElementType.*;
020import static java.lang.annotation.RetentionPolicy.*;
021
022import java.lang.annotation.*;
023
024import org.junit.jupiter.api.*;
025import org.junit.jupiter.api.extension.*;
026
027/**
028 * Boots a whole {@link org.apache.juneau.microservice.Microservice Microservice} (config + lifecycle +
029 * embedded Jetty server) for the annotated JUnit 5 test class — the standalone-microservice analog of
030 * Spring's {@code @SpringBootTest}.
031 *
032 * <p>
033 * {@code @MicroserviceTest} is a single, server-agnostic meta-annotation: it composes
034 * {@link ExtendWith @ExtendWith}({@link MicroserviceExtension}) plus a {@code @Tag("microservice")} marker.
035 * The extension builds the microservice on an OS-assigned ephemeral port before the test class runs, and
036 * {@link org.apache.juneau.microservice.Microservice#stop() stop()}s it afterward (releasing the port and
037 * firing {@code @PreDestroy} hooks).
038 *
039 * <h5 class='topic'>Specifying the system under test</h5>
040 *
041 * <p>
042 * The SUT is declared <i>explicitly</i> (no classpath scanning), two complementary ways:
043 * <ol>
044 *    <li>{@link #configurations()} &mdash; one or more {@code @Configuration} classes whose {@code @Bean Servlet}
045 *       methods are auto-mounted by the microservice (the common case).
046 *    <li>A {@code static} method on the test class returning a
047 *       {@link org.apache.juneau.microservice.Microservice.Builder Microservice.Builder} &mdash; for full control
048 *       over the builder. Discovered by name {@value MicroserviceExtension#BUILDER_SUPPLIER_METHOD} (override via
049 *       {@link #builderMethod()}). Configurations from {@link #configurations()} are appended to whatever the
050 *       supplier returns.
051 * </ol>
052 *
053 * <p>
054 * In both cases the extension additionally installs an {@link EphemeralJettyServerConfig} (binding a Jetty
055 * {@code Server} to port 0) and {@code JettyConfiguration}, so tests only contribute their resources.
056 *
057 * <h5 class='topic'>Mock-bean injection</h5>
058 *
059 * <p>
060 * Declare collaborators to substitute with the existing {@link org.apache.juneau.bct.junit.TestBean @TestBean}
061 * (the {@code @MockBean} analog) &mdash; no parallel injection annotation. By default these are installed via
062 * <b>Mode INJECT</b> (through {@code Microservice.Builder.overridingBeanStore(...)}) <i>before</i> boot, so the
063 * service sees them from startup. {@code @TestBean(mode = Mode.OVERLAY)} pushes/pops against the already-booted
064 * instance instead.
065 *
066 * <h5 class='topic'>What gets injected into tests</h5>
067 *
068 * <p>
069 * Test methods (and lifecycle methods) may declare parameters resolved by the extension:
070 * {@link org.apache.juneau.rest.client.RestClient RestClient} (bound to the booted server's root URL &mdash; the
071 * primary convenience), {@link org.apache.juneau.microservice.Microservice Microservice}, the
072 * {@link org.apache.juneau.commons.inject.WritableBeanStore WritableBeanStore}, and the bound port (as {@code int}
073 * / {@code Integer}). The same {@code TestBeanStore} parameter the underlying
074 * {@link org.apache.juneau.bct.junit.JuneauBeanStoreExtension} resolves is available too.
075 *
076 * <h5 class='topic'>When to use this vs. {@code MockRestClient}</h5>
077 *
078 * <p>
079 * Use {@code @MicroserviceTest} for a genuine full-microservice integration test &mdash; real server, real
080 * connectors, real lifecycle, over HTTP. For an in-JVM test of a single {@code @Rest} resource (no server, no
081 * sockets), use {@code MockRestClient} directly; the two paths are intentionally distinct.
082 *
083 * <h5 class='section'>Example:</h5>
084 * <p class='bjava'>
085 *    <ja>@MicroserviceTest</ja>(configurations=MyServerConfig.<jk>class</jk>)
086 *    <jk>class</jk> MyServiceTest {
087 *
088 *       <ja>@Configuration</ja>
089 *       <jk>static class</jk> MyServerConfig {
090 *          <ja>@Bean</ja> Servlet myService() { <jk>return new</jk> MyRestService(); }
091 *       }
092 *
093 *       <ja>@TestBean</ja>
094 *       MyExternalApi <jv>mockApi</jv> = Mockito.<jsm>mock</jsm>(MyExternalApi.<jk>class</jk>);
095 *
096 *       <ja>@Test</ja>
097 *       <jk>void</jk> aTest(RestClient <jv>client</jv>) {
098 *          <jv>client</jv>.get(<js>"/widgets/1"</js>).run().assertStatus().is(200);
099 *       }
100 *    }
101 * </p>
102 *
103 * <h5 class='section'>See Also:</h5><ul>
104 *    <li class='jc'>{@link MicroserviceExtension}
105 *    <li class='jc'>{@link org.apache.juneau.bct.junit.TestBean}
106 * </ul>
107 *
108 * @since 10.0.0
109 */
110@Documented
111@Retention(RUNTIME)
112@Target(TYPE)
113@ExtendWith(MicroserviceExtension.class)
114@Tag("microservice")
115public @interface MicroserviceTest {
116
117   /**
118    * The {@code @Configuration} classes whose {@code @Bean Servlet} methods the microservice auto-mounts.
119    *
120    * <p>
121    * Appended after any builder produced by a {@link #builderMethod()} supplier, and before the framework's
122    * {@link EphemeralJettyServerConfig} + {@code JettyConfiguration}.
123    *
124    * @return The configuration classes. Empty by default (rely on a {@link #builderMethod()} supplier).
125    */
126   Class<?>[] configurations() default {};
127
128   /**
129    * Name of an optional {@code static} method on the test class returning a
130    * {@link org.apache.juneau.microservice.Microservice.Builder Microservice.Builder} to seed the boot.
131    *
132    * <p>
133    * The method must be {@code static}, take no arguments, and return a {@code Microservice.Builder}. When absent,
134    * a fresh {@code Microservice.create()} builder is used. {@link #configurations()} are appended either way.
135    *
136    * @return The supplier method name. Defaults to {@value MicroserviceExtension#BUILDER_SUPPLIER_METHOD}.
137    */
138   String builderMethod() default MicroserviceExtension.BUILDER_SUPPLIER_METHOD;
139}