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
019
020import java.util.function.*;
021
022import org.apache.juneau.commons.inject.*;
023import static org.apache.juneau.commons.utils.Shorts.*;
024
025/**
026 * Fluent {@link BasicBeanStore} subclass for declaring test-time bean overrides.
027 *
028 * <p>
029 * A {@code TestBeanStore} is an overlay-builder.  Tests register replacements for production beans
030 * via {@link #override} and then plug the resulting store into the SUT &mdash; typically through a
031 * builder hook such as {@code MockRestClient.Builder.overridingBeanStore(...)} (the Mode INJECT wiring
032 * pattern; see {@link JuneauBeanStoreExtension} for the full contract).  The framework threads the
033 * overlay into the {@code overridingParent} slot of the SUT's bean store, putting registered overrides
034 * at tier 1 of the resolution chain (above local resource entries and the Spring bridge).
035 *
036 * <p>
037 * <b>No JUnit 5 dependency.</b>  This class is intentionally a plain {@link BasicBeanStore} subclass
038 * so that callers from non-JUnit harnesses (TestNG, raw {@code main(...)}, Spring Test, etc.) can
039 * use it without dragging in {@code junit-jupiter-api}.
040 *
041 * <h5 class='section'>Example:</h5>
042 * <p class='bjava'>
043 *    <jc>// Build the overlay.</jc>
044 *    <jk>var</jk> <jv>overlay</jv> = <jk>new</jk> TestBeanStore()
045 *       .override(MyExternalApi.<jk>class</jk>, mockApi)
046 *       .override(RichLogger.<jk>class</jk>, () -&gt; spyLogger);
047 *
048 *    <jc>// Wire into the SUT.</jc>
049 *    <jk>try</jk> (<jv>client</jv> = MockRestClient.<jsm>create</jsm>(MyResource.<jk>class</jk>)
050 *          .overridingBeanStore(<jv>overlay</jv>)
051 *          .build()) {
052 *       <jv>client</jv>.get(<js>"/widgets/1"</js>).run().assertStatus().is(200);
053 *    }
054 * </p>
055 *
056 * <h5 class='section'>See Also:</h5><ul>
057 *    <li class='jc'>{@link BasicBeanStore} - The bean store this overlay extends.
058 *    <li class='jc'>{@link StackOverlay} - The stack-aware composition primitive used for class-scope and Mode OVERLAY layering.
059 *    <li class='jc'>{@link JuneauBeanStoreExtension} - Mode INJECT vs Mode OVERLAY contracts and the JUnit 5 lifecycle owner.
060 * </ul>
061 *
062 * @since 10.0.0
063 */
064@SuppressWarnings({
065   "resource" // Fluent override(...) methods return this (a Closeable BeanStore) owned by the caller/SUT; Eclipse JDT @Owning warning is by design.
066})
067public class TestBeanStore extends BasicBeanStore {
068
069   /**
070    * Constructor.
071    */
072   public TestBeanStore() {
073      super();
074   }
075
076   /**
077    * Parent-chaining constructor.
078    *
079    * <p>
080    * Lookups consult this store's local entries first and then fall through to the supplied parent.  Used by
081    * {@link JuneauBeanStoreExtension} to stack a per-method overlay on top of a per-class overlay so method-scope
082    * overrides shadow class-scope overrides of the same {@code (type, name)} pair.
083    *
084    * @param parent The parent bean store.  Can be <jk>null</jk> for a standalone overlay.
085    * @since 10.0.0
086    */
087   public TestBeanStore(BeanStore parent) {
088      super(parent);
089   }
090
091   /**
092    * Registers an unnamed bean as a test-time override.
093    *
094    * @param <T> The bean type.
095    * @param type The bean type.  Must not be <jk>null</jk>.
096    * @param bean The override instance.  Can be <jk>null</jk> (stored as an explicit <jk>null</jk> binding, distinct from no binding at all).
097    * @return This object.
098    */
099   public <T> TestBeanStore override(Class<T> type, T bean) {
100      reqnn("type", type);
101      addBean(type, bean);
102      return this;
103   }
104
105   /**
106    * Registers a named bean as a test-time override.
107    *
108    * @param <T> The bean type.
109    * @param type The bean type.  Must not be <jk>null</jk>.
110    * @param bean The override instance.  Can be <jk>null</jk> (stored as an explicit <jk>null</jk> binding, distinct from no binding at all).
111    * @param name The bean name.  Can be <jk>null</jk> for unnamed beans.
112    * @return This object.
113    */
114   public <T> TestBeanStore override(Class<T> type, T bean, String name) {
115      reqnn("type", type);
116      addBean(type, bean, name);
117      return this;
118   }
119
120   /**
121    * Registers an unnamed supplier-backed bean as a test-time override.
122    *
123    * <p>
124    * The supplier is invoked lazily on first lookup, mirroring {@link BasicBeanStore#addSupplier}.
125    *
126    * @param <T> The bean type.
127    * @param type The bean type.  Must not be <jk>null</jk>.
128    * @param supplier The override supplier.  Must not be <jk>null</jk>.
129    * @return This object.
130    */
131   public <T> TestBeanStore override(Class<T> type, Supplier<T> supplier) {
132      reqnn("type", type);
133      reqnn("supplier", supplier);
134      addSupplier(type, supplier);
135      return this;
136   }
137
138   /**
139    * Registers a named supplier-backed bean as a test-time override.
140    *
141    * <p>
142    * The supplier is invoked lazily on first lookup, mirroring {@link BasicBeanStore#addSupplier}.
143    *
144    * @param <T> The bean type.
145    * @param type The bean type.  Must not be <jk>null</jk>.
146    * @param supplier The override supplier.  Must not be <jk>null</jk>.
147    * @param name The bean name.  Can be <jk>null</jk> for unnamed beans.
148    * @return This object.
149    */
150   public <T> TestBeanStore override(Class<T> type, Supplier<T> supplier, String name) {
151      reqnn("type", type);
152      reqnn("supplier", supplier);
153      addSupplier(type, supplier, name);
154      return this;
155   }
156}