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