GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.

io.github.glytching.junit.extension.random.RandomBeansExtension   A
last analyzed

Complexity

Total Complexity 14

Size/Duplication

Total Lines 134
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 134
rs 10
c 1
b 0
f 0
eloc 49
wmc 14

6 Methods

Rating   Name   Duplication   Size   Complexity  
A supportsParameter(ParameterContext,ExtensionContext) 0 5 1
A resolveParameter(ParameterContext,ExtensionContext) 0 7 1
A RandomBeansExtension(EnhancedRandom) 0 2 1
A resolve(Class,Random) 0 13 5
A postProcessTestInstance(Object,ExtensionContext) 0 11 5
A RandomBeansExtension() 0 27 1
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 * contributor license agreements.  See the NOTICE file distributed with
4
 * this work for additional information regarding copyright ownership.
5
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 * (the "License"); you may not use this file except in compliance with
7
 * the License.  You may obtain a copy of the License at
8
 *
9
 *      http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
package io.github.glytching.junit.extension.random;
18
19
import io.github.benas.randombeans.EnhancedRandomBuilder;
20
import io.github.benas.randombeans.api.EnhancedRandom;
21
import org.junit.jupiter.api.extension.*;
22
23
import java.lang.reflect.Field;
24
import java.lang.reflect.Modifier;
25
import java.util.Collection;
26
import java.util.List;
27
import java.util.Set;
28
import java.util.stream.Collectors;
29
import java.util.stream.Stream;
30
31
import static java.nio.charset.Charset.forName;
32
import static org.junit.platform.commons.support.AnnotationSupport.isAnnotated;
33
34
/**
35
 * The random beans extension provides a test with randomly generated objects, including:
36
 *
37
 * <ul>
38
 *   <li>JDK types
39
 *   <li>Custom types
40
 *   <li>Collections
41
 *   <li>Generic collections
42
 *   <li>Partial population
43
 * </ul>
44
 *
45
 * <p>Usage examples:
46
 *
47
 * <p>Injecting random values as fields:
48
 *
49
 * <pre>
50
 * &#064;ExtendWith(RandomBeansExtension.class)
51
 * public class MyTest {
52
 *
53
 *     &#064;Random
54
 *     private String anyString;
55
 *
56
 *     &#064;Random(excluded = {"name", "value"})
57
 *     private List<DomainObject> anyPartiallyPopulatedDomainObject;
58
 *
59
 *     &#064;Random(type = DomainObject.class)
60
 *     private List<DomainObject> anyDomainObjects;
61
 *
62
 *     &#064;Test
63
 *     public void testUsingRandomString() {
64
 *         // use the injected anyString
65
 *         // ...
66
 *     }
67
 *
68
 *     &#064;Test
69
 *     public void testUsingRandomDomainObjects() {
70
 *         // use the injected anyDomainObjects
71
 *         // the anyDomainObjects will contain _N_ fully populated random instances of DomainObject
72
 *         // ...
73
 *     }
74
 *
75
 *     &#064;Test
76
 *     public void testUsingPartiallyPopulatedDomainObject() {
77
 *         // use the injected anyPartiallyPopulatedDomainObject
78
 *         // this object's "name" and "value" members will not be populated since this has been declared with
79
 *         //     excluded = {"name", "value"}
80
 *         // ...
81
 *     }
82
 * }
83
 * </pre>
84
 *
85
 * <p>Injecting random values as parameters:
86
 *
87
 * <pre>
88
 * &#064;ExtendWith(RandomBeansExtension.class)
89
 * public class MyTest {
90
 *
91
 *     &#064;Test
92
 *     &#064;ExtendWith(RandomBeansExtension.class)
93
 *     public void testUsingRandomString(&#064;Random String anyString) {
94
 *         // use the provided anyString
95
 *         // ...
96
 *     }
97
 *
98
 *     &#064;Test
99
 *     &#064;ExtendWith(RandomBeansExtension.class)
100
 *     public void testUsingRandomDomainObjects(&#064;Random(type = DomainObject.class) List<DomainObject> anyDomainObjects) {
101
 *         // use the injected anyDomainObjects
102
 *         // the anyDomainObjects will contain _N_ fully populated random instances of DomainObject
103
 *         // ...
104
 *     }
105
 *
106
 *     &#064;Test
107
 *     &#064;ExtendWith(RandomBeansExtension.class)
108
 *     public void testUsingPartiallyPopulatedDomainObject(&#064;Random(excluded = {"name", "value"}) List<DomainObject> anyPartiallyPopulatedDomainObject) {
109
 *         // use the injected anyPartiallyPopulatedDomainObject
110
 *         // this object's "name" and "value" members will not be populated since this has been declared with
111
 *         //     excluded = {"name", "value"}
112
 *         // ...
113
 *     }
114
 * }
115
 * </pre>
116
 *
117
 * @see <a href="https://github.com/benas/random-beans">Random Beans</a>
118
 * @since 1.0.0
119
 */
120
public class RandomBeansExtension implements TestInstancePostProcessor, ParameterResolver {
121
122
  private final EnhancedRandom random;
123
124
  /**
125
   * Create the extension with a default {@link EnhancedRandom}.
126
   *
127
   * @see <a href="https://github.com/benas/random-beans/wiki/Randomization-parameters">Enhanced
128
   *     Random Configuration Parameters</a>
129
   */
130
  public RandomBeansExtension() {
131
    this(EnhancedRandomBuilder.aNewEnhancedRandomBuilder()
132
            // maximum number of instances of a given type, above this number requests will start to
133
            // reuse
134
            // previously generated instances
135
            .objectPoolSize(10)
136
137
            // how deep should we go when randomising an object graph?
138
            .randomizationDepth(5)
139
140
            // the charset used for all String and Character values
141
            .charset(forName("UTF-8"))
142
143
            // min, max bounds for the generated string length
144
            .stringLengthRange(5, 50)
145
146
            // min, max bounds for the generated collections size
147
            .collectionSizeRange(1, 10)
148
149
            // if a random values is declared as an abstract or interface type then the classpath
150
            // will be scanned
151
            // for a concrete type of that abstract or interface type
152
            .scanClasspathForConcreteTypes(true)
153
154
            // do not override any values which are already initialised in the target type
155
            .overrideDefaultInitialization(false)
156
            .build());
157
  }
158
159
  /**
160
   * Create the extension with the given {@link EnhancedRandom}. This is used, instead of the zero-arg alternative, when
161
   * the caller wants to override the default 'randomizer' configuration. This constructor will be called by using the
162
   * {@code RegisterExtension} annotation.
163
   *
164
   * @param enhancedRandom
165
   * @since 2.5.0
166
   */
167
  public RandomBeansExtension(EnhancedRandom enhancedRandom) {
168
    this.random = enhancedRandom;
169
170
  }
171
172
  /**
173
   * Does this extension support injection for parameters of the type described by the given {@code
174
   * parameterContext}?
175
   *
176
   * @param parameterContext the context for the parameter for which an argument should be resolved
177
   * @param extensionContext the extension context for the {@code Executable} about to be invoked
178
   * @return true if the given {@code parameterContext} is annotated with {@link Random}, false
179
   *     otherwise
180
   * @throws ParameterResolutionException
181
   */
182
  @Override
183
  public boolean supportsParameter(
184
      ParameterContext parameterContext, ExtensionContext extensionContext)
185
      throws ParameterResolutionException {
186
    return parameterContext.getParameter().getAnnotation(Random.class) != null;
187
  }
188
189
  /**
190
   * Provides a value for any parameter context which has passed the {@link
191
   * #supportsParameter(ParameterContext, ExtensionContext)} gate.
192
   *
193
   * @param parameterContext the context for the parameter for which an argument should be resolved
194
   * @param extensionContext the <em>context</em> in which the current test or container is being
195
   *     executed
196
   * @return a randomly generated object
197
   * @throws ParameterResolutionException
198
   */
199
  @Override
200
  public Object resolveParameter(
201
      ParameterContext parameterContext, ExtensionContext extensionContext)
202
      throws ParameterResolutionException {
203
    return resolve(
204
        parameterContext.getParameter().getType(),
205
        parameterContext.getParameter().getAnnotation(Random.class));
206
  }
207
208
  /**
209
   * Inject random values into any fields which are annotated with {@link Random}. 
210
   * This method doesn't populate static fields if they have values.
211
   *
212
   * @param testInstance the instance to post-process
213
   * @param extensionContext the current extension context
214
   * @throws Exception
215
   */
216
  @Override
217
  public void postProcessTestInstance(Object testInstance, ExtensionContext extensionContext)
218
      throws Exception {
219
    for (Field field : testInstance.getClass().getDeclaredFields()) {
220
      if (isAnnotated(field, Random.class)) {
221
        Random annotation = field.getAnnotation(Random.class);
222
        Object randomObject = resolve(field.getType(), annotation);
223
        
224
        field.setAccessible(true);
225
        if (!Modifier.isStatic(field.getModifiers()) || field.get(testInstance) == null) {
226
          field.set(testInstance, randomObject);
227
        }
228
      }
229
    }
230
  }
231
232
  /**
233
   * Maps the 'random requirements' expressed by the given {@code annotation} to invocations on
234
   * {@link #random}.
235
   *
236
   * @param targetType the type to be provided
237
   * @param annotation an instance of {@link Random} which describes how the user wishes to
238
   *     configure the 'random generation'
239
   * @return a randomly generated instance of {@code targetType}
240
   */
241
  private Object resolve(Class<?> targetType, Random annotation) {
242
    if (targetType.isAssignableFrom(List.class) || targetType.isAssignableFrom(Collection.class)) {
243
      return random
244
          .objects(annotation.type(), annotation.size(), annotation.excludes())
245
          .collect(Collectors.toList());
246
    } else if (targetType.isAssignableFrom(Set.class)) {
247
      return random
248
          .objects(annotation.type(), annotation.size(), annotation.excludes())
249
          .collect(Collectors.toSet());
250
    } else if (targetType.isAssignableFrom(Stream.class)) {
251
      return random.objects(annotation.type(), annotation.size(), annotation.excludes());
252
    } else {
253
      return random.nextObject(targetType, annotation.excludes());
254
    }
255
  }
256
257
}
258