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()} — 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} — 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) — 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 — 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 — 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}