1 /*
2 * SPDX-License-Identifier: Apache-2.0
3 * See LICENSE file for details.
4 *
5 * Copyright 2012-2026 hazendaz
6 *
7 * Portions of initial baseline code (getter/setter test) by Rob Dawson (CodeBox)
8 */
9 package com.codebox.bean;
10
11 import com.codebox.enums.CheckClear;
12 import com.codebox.enums.CheckConstructor;
13 import com.codebox.enums.CheckEquals;
14 import com.codebox.enums.CheckSerialize;
15 import com.codebox.enums.LoadData;
16 import com.codebox.enums.SkipStrictSerialize;
17 import com.codebox.instance.ConstructorInstance;
18
19 import java.util.Arrays;
20
21 /**
22 * Fluent configuration object returned by {@link JavaBeanTester#builder(Class)}.
23 *
24 * <p>
25 * Configure optional checks by chaining methods such as {@link #checkEquals()}, {@link #checkSerializable()},
26 * {@link #loadData()}, or {@link #skip(String...)}, then finish with a terminal method such as {@link #test()},
27 * {@link #testInstance(Object)}, {@link #testEquals(Object, Object)}, {@link #testObjectMethods()}, or
28 * {@link #testPrivateConstructor()}.
29 *
30 * @param <T>
31 * the type under test
32 * @param <E>
33 * the extension type used for equality-related checks
34 */
35 public class JavaBeanTesterBuilder<T, E> {
36
37 /** The worker. */
38 private final JavaBeanTesterWorker<T, E> worker;
39
40 /**
41 * Instantiates a new java bean tester builder.
42 *
43 * @param clazz
44 * the clazz
45 */
46 JavaBeanTesterBuilder(final Class<T> clazz) {
47 this.worker = new JavaBeanTesterWorker<>(clazz);
48 }
49
50 /**
51 * Instantiates a new java bean tester builder.
52 *
53 * @param clazz
54 * the clazz
55 * @param extension
56 * the extension
57 */
58 JavaBeanTesterBuilder(final Class<T> clazz, final Class<E> extension) {
59 this.worker = new JavaBeanTesterWorker<>(clazz, extension);
60 }
61
62 /**
63 * Enables {@code clear()} verification.
64 *
65 * @return this builder
66 */
67 public JavaBeanTesterBuilder<T, E> checkClear() {
68 return this.checkClear(true);
69 }
70
71 /**
72 * Toggles {@code clear()} verification.
73 *
74 * @param value
75 * {@code true} to enable the check, {@code false} to disable it
76 *
77 * @return this builder
78 */
79 public JavaBeanTesterBuilder<T, E> checkClear(final boolean value) {
80 this.worker.setCheckClear(value ? CheckClear.ON : CheckClear.OFF);
81 return this;
82 }
83
84 /**
85 * Enables public-constructor verification.
86 *
87 * @return this builder
88 */
89 public JavaBeanTesterBuilder<T, E> checkConstructor() {
90 return this.checkConstructor(true);
91 }
92
93 /**
94 * Toggles public-constructor verification.
95 *
96 * @param value
97 * {@code true} to enable the check, {@code false} to disable it
98 *
99 * @return this builder
100 */
101 public JavaBeanTesterBuilder<T, E> checkConstructor(final boolean value) {
102 this.worker.setCheckConstructor(value ? CheckConstructor.ON : CheckConstructor.OFF);
103 return this;
104 }
105
106 /**
107 * Enables {@code equals}, {@code hashCode}, {@code toString}, and {@code canEqual} verification.
108 *
109 * @return this builder
110 */
111 public JavaBeanTesterBuilder<T, E> checkEquals() {
112 return this.checkEquals(true);
113 }
114
115 /**
116 * Toggles {@code equals}, {@code hashCode}, {@code toString}, and {@code canEqual} verification.
117 *
118 * @param value
119 * {@code true} to enable the check, {@code false} to disable it
120 *
121 * @return this builder
122 */
123 public JavaBeanTesterBuilder<T, E> checkEquals(final boolean value) {
124 this.worker.setCheckEquals(value ? CheckEquals.ON : CheckEquals.OFF);
125 return this;
126 }
127
128 /**
129 * Requires the type under test to be serializable and validates a serialization round-trip.
130 *
131 * @return this builder
132 */
133 public JavaBeanTesterBuilder<T, E> checkSerializable() {
134 return this.checkSerializable(true);
135 }
136
137 /**
138 * Toggles strict serializable validation.
139 *
140 * @param value
141 * {@code true} to require serialization support, {@code false} to skip the strict requirement
142 *
143 * @return this builder
144 */
145 public JavaBeanTesterBuilder<T, E> checkSerializable(final boolean value) {
146 this.worker.setCheckSerializable(value ? CheckSerialize.ON : CheckSerialize.OFF);
147 return this;
148 }
149
150 /**
151 * Enables recursive sample-data generation for supported property types.
152 *
153 * @return this builder
154 */
155 public JavaBeanTesterBuilder<T, E> loadData() {
156 return this.loadData(true);
157 }
158
159 /**
160 * Toggles recursive sample-data generation for supported property types.
161 *
162 * @param value
163 * {@code true} to enable generated data, {@code false} to disable it
164 *
165 * @return this builder
166 */
167 public JavaBeanTesterBuilder<T, E> loadData(final boolean value) {
168 this.worker.setLoadData(value ? LoadData.ON : LoadData.OFF);
169 return this;
170 }
171
172 /**
173 * Skip Strict Serializable is intended to relax strict check on serializable objects. For complex objects, strict
174 * checking will result in issues with equals check. Testing has shown this to be generally not a normal use case of
175 * javabean tester as it is normally used with POJOs only. In such a case, caller will get an error and if there is
176 * not actually a code problem they should turn this skip on.
177 *
178 * @return the java bean tester builder
179 */
180 public JavaBeanTesterBuilder<T, E> skipStrictSerializable() {
181 this.worker.setSkipStrictSerializable(SkipStrictSerialize.ON);
182 return this;
183 }
184
185 /**
186 * Skips the supplied bean properties during getter/setter validation.
187 *
188 * @param propertyNames
189 * the bean property names to exclude
190 *
191 * @return this builder
192 */
193 public JavaBeanTesterBuilder<T, E> skip(final String... propertyNames) {
194 if (propertyNames != null) {
195 this.worker.getSkipThese().addAll(Arrays.asList(propertyNames));
196 }
197 return this;
198 }
199
200 /**
201 * Runs the configured bean validation flow.
202 */
203 public void test() {
204 this.worker.test();
205 }
206
207 /**
208 * Verifies that a private constructor can be exercised.
209 */
210 public void testPrivateConstructor() {
211 ConstructorInstance.inaccessible(this.worker.getClazz());
212 }
213
214 /**
215 * Runs only the object-method checks for the configured class.
216 */
217 public void testObjectMethods() {
218 this.worker.equalsHashCodeToStringSymmetricTest();
219 }
220
221 /**
222 * Runs getter/setter checks against the supplied instance.
223 *
224 * @param instance
225 * the instance to populate and verify
226 */
227 public void testInstance(final T instance) {
228 this.worker.getterSetterTests(instance);
229 }
230
231 /**
232 * Compares two prepared instances using the configured equality options.
233 *
234 * @param instance
235 * the instance under test
236 * @param expected
237 * the expected comparison instance
238 */
239 public void testEquals(final T instance, final T expected) {
240 this.worker.equalsTests(instance, expected);
241 }
242
243 }