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.bct.junit; 018 019import static java.lang.annotation.ElementType.*; 020import static java.lang.annotation.RetentionPolicy.*; 021 022import java.lang.annotation.*; 023 024/** 025 * Marks a field or method on a JUnit 5 test class as a test-time bean override. 026 * 027 * <p> 028 * {@code @TestBean} is the Juneau analog of Spring's {@code @MockBean} / {@code @TestConfiguration}, scoped to a 029 * {@code BeanStore} overlay rather than a Spring application context. The {@link JuneauBeanStoreExtension} discovers 030 * {@code @TestBean}-annotated members on the test class, builds a {@link TestBeanStore} overlay containing the 031 * declared overrides, and exposes it via {@link JuneauBeanStoreExtension#getStore()} so the test author can wire 032 * it into the SUT explicitly (typically via a builder hook such as 033 * {@code MockRestClient.Builder.overridingBeanStore(...)}). 034 * 035 * <p> 036 * The wiring is intentionally <i>explicit</i>: the extension does not auto-discover {@code RestContext} / 037 * {@code Microservice} fields on the test class. Test authors write one line of plumbing 038 * ({@code .overridingBeanStore(ext.getStore())}) and get full control. 039 * 040 * <p> 041 * The annotation supports two wiring modes via {@link #mode()}: <b>Mode INJECT</b> (default, fresh-instance with 042 * overrides via the SUT's builder) and <b>Mode OVERLAY</b> (existing-instance push/pop against a long-lived 043 * SUT's bean store). See {@link JuneauBeanStoreExtension} for the full Mode INJECT / Mode OVERLAY contracts. 044 * 045 * <h5 class='section'>Applies to:</h5> 046 * <ul> 047 * <li><b>Fields</b> — the field's current value (read via reflection) is registered as the override. 048 * <li><b>Methods</b> — a parameterless factory method whose return type is type-compatible with the 049 * override target. Invoked once per scope (per-method for {@link Scope#METHOD}, per-class for 050 * {@link Scope#CLASS}). 051 * </ul> 052 * 053 * <h5 class='section'>Example:</h5> 054 * <p class='bjava'> 055 * <ja>@ExtendWith</ja>(JuneauBeanStoreExtension.<jk>class</jk>) 056 * <jk>class</jk> MyResourceTest { 057 * 058 * <ja>@TestBean</ja> 059 * MyExternalApi <jv>mockApi</jv> = Mockito.<jsm>mock</jsm>(MyExternalApi.<jk>class</jk>); 060 * 061 * <ja>@TestBean</ja>(name=<js>"primary"</js>) 062 * <jk>static</jk> MyService primaryService() { <jk>return new</jk> InMemoryMyService(<js>"p"</js>); } 063 * 064 * <ja>@Test</ja> 065 * <jk>void</jk> aTest(TestBeanStore <jv>store</jv>) { 066 * <jk>var</jk> <jv>client</jv> = MockRestClient.<jsm>create</jsm>(MyResource.<jk>class</jk>) 067 * .overridingBeanStore(<jv>store</jv>) 068 * .build(); 069 * <jv>client</jv>.get(<js>"/widgets/1"</js>).run().assertStatus().is(200); 070 * } 071 * } 072 * </p> 073 * 074 * <h5 class='section'>See Also:</h5><ul> 075 * <li class='jc'>{@link JuneauBeanStoreExtension} - The JUnit 5 extension that discovers and applies these annotations. 076 * <li class='jc'>{@link TestBeanStore} - The overlay builder the extension populates. 077 * <li class='je'>{@link Scope} - Per-method vs per-class lifecycle selector. 078 * </ul> 079 * 080 * @since 10.0.0 081 */ 082@Documented 083@Retention(RUNTIME) 084@Target({ FIELD, METHOD }) 085public @interface TestBean { 086 087 /** 088 * Qualifier name for the override. 089 * 090 * <p> 091 * Empty string (the default) means the override is registered as the <i>unnamed</i> bean of its type, matching 092 * the behavior of {@link TestBeanStore#override(Class, Object)} without a name argument. 093 * 094 * <p> 095 * When non-empty, the override is registered under this qualifier so the framework's 096 * {@code @Bean(name = "...")} parameter resolution can find it. Two {@code @TestBean}-declared overrides of 097 * the same type but different names are both picked up — the qualifier is the disambiguator. 098 * 099 * @return The qualifier name. Empty string means "unnamed". 100 * @since 10.0.0 101 */ 102 String name() default ""; 103 104 /** 105 * Explicit override target type. 106 * 107 * <p> 108 * {@code Object.class} (the default) is a sentinel meaning <i>"use the annotated member's declared type"</i> 109 * — the field's declared type for field overrides, the method's return type for method overrides. This 110 * is the common case. 111 * 112 * <p> 113 * Set this explicitly when the member's declared type is a supertype of the intended registration type, for 114 * example an {@code Object} field carrying a {@code MyService} value, or a {@code @TestBean Supplier<MyService>} 115 * factory method whose semantic type is {@code MyService}. 116 * 117 * @return The override target type. {@code Object.class} means "use the member's declared type". 118 * @since 10.0.0 119 */ 120 Class<?> type() default Object.class; 121 122 /** 123 * Lifecycle scope of the override. 124 * 125 * <p> 126 * Defaults to {@link Scope#METHOD} — the override is rebuilt for every test method. Use 127 * {@link Scope#CLASS} to share the override across all test methods in the class. 128 * 129 * <p> 130 * {@code CLASS}-scope overrides may only be declared on {@code static} fields and {@code static} methods. 131 * Instance members are not visible at {@code beforeAll} time and will be rejected with an error. 132 * 133 * @return The lifecycle scope. 134 * @since 10.0.0 135 */ 136 Scope scope() default Scope.METHOD; 137 138 /** 139 * Wiring mode for the override. 140 * 141 * <p> 142 * Defaults to {@link Mode#INJECT} — the override is installed at SUT construction time via the SUT's 143 * {@code overridingBeanStore(...)} builder hook. Use {@link Mode#OVERLAY} for push/pop overlays against a 144 * long-lived SUT that the test has 145 * {@linkplain JuneauBeanStoreExtension#attach(org.apache.juneau.commons.inject.WritableBeanStore) attach}-ed 146 * to the extension. 147 * 148 * <p> 149 * Mixing {@code Mode.INJECT} and {@code Mode.OVERLAY} annotations on the same scope is rejected at scope-build 150 * time with a clear {@link IllegalStateException}. 151 * 152 * @return The wiring mode. 153 * @since 10.0.0 154 */ 155 Mode mode() default Mode.INJECT; 156}