diff --git a/core/build.gradle b/core/build.gradle index 28551f0..b4a6eb3 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -1,5 +1,6 @@ dependencies { testImplementation dependency.junit_jupiter_api + testImplementation dependency.lombok testRuntimeOnly dependency.junit_jupiter_engine } diff --git a/core/src/main/java/com/tngtech/valueprovider/ValueProvider.java b/core/src/main/java/com/tngtech/valueprovider/ValueProvider.java index 51187c4..eb59e71 100644 --- a/core/src/main/java/com/tngtech/valueprovider/ValueProvider.java +++ b/core/src/main/java/com/tngtech/valueprovider/ValueProvider.java @@ -12,6 +12,7 @@ import java.time.LocalTime; import java.time.Month; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.EnumSet; import java.util.List; @@ -735,7 +736,8 @@ public > Set someOf(Class enumClass, int numberOfElement * @param elements the elements to draw from. * @return the drawn elements (none / some / all). */ - @SafeVarargs final public Collection someOf(T... elements) { + @SafeVarargs + final public Collection someOf(T... elements) { return someOf(asList(elements)); } @@ -829,6 +831,136 @@ public final T oneOfExcluding(Iterable elements, T... elementsToExclude) return oneOf(allElements); } + /** + * Generates a {@link List} of <T> (by means of {@code generator}) and includes {@code containedElements} in the {@link List}. + *

+ * Example: + *

+     *          static class MyBeanTestData {
+     *              public static MyBean myBean(ValueProvider valueProvider) {
+     *                  // builds and returns your bean
+     *              }
+     *          }
+     *
+     *         ValueProvider vp = ValueProviderFactory.createRandomValueProvider();
+     *         vp.listOfContaining(MyBeanTestData::myBean, myBean(), myBean(), myBean()); // -> List[myBean_1, myBean_2, myBean_random, myBean_3, myBean_random]
+     * 
+ *

+ * + * @param generator a generator {@link Function} to generate T given a {@link ValueProvider}. + * @param containedElements elements that should be contained in the generated list. + * + * @return the generated {@link List}. + * + * @see #listOf(Function) + */ + @SafeVarargs + public final List listOfContaining(Function generator, T... containedElements) { + return listOfContaining(generator, Arrays.asList(containedElements)); + } + + /** + * see {@link #listOfContaining(Function, Object[])} + */ + public final List listOfContaining(Function generator, Collection containedElements) { + int maxNumberOfRandomElements = maxNumberOfRandomElements(containedElements); + + List generatedElements = new ArrayList<>(); + for (T containedValue : containedElements) { + generatedElements.addAll(listOf(intNumber(0, maxNumberOfRandomElements), generator)); + generatedElements.add(containedValue); + } + generatedElements.addAll(listOf(intNumber(0, maxNumberOfRandomElements), generator)); + return generatedElements; + } + + private int maxNumberOfRandomElements(Collection containedElements) { + if (containedElements.size() == 0) { + return 5; + } + if (containedElements.size() == 1) { + return 2; + } + return 1; + } + + /** + * Generates a {@link List} of <T> (by means of {@code generator}). Ensures that the {@link List} contains at least one element. + *

+ * Example: + *

+     *          static class MyBeanTestData {
+     *              public static MyBean myBean(ValueProvider valueProvider) {
+     *                  // builds and returns your bean
+     *              }
+     *          }
+     *
+     *         ValueProvider vp = ValueProviderFactory.createRandomValueProvider();
+     *         vp.nonEmptyListOf(MyBeanTestData::myBean); // -> List[myBean_generated_1, myBean_generated_2]
+     * 
+ *

+ * + * @param generator a generator {@link Function} to generate T given a {@link ValueProvider}. + * + * @return the generated {@link List}. + */ + public List nonEmptyListOf(Function generator) { + return listOf(intNumber(1, 5), generator); + } + + /** + * Generates a {@link List} of <T> (by means of {@code generator}). Might return the empty list. + *

+ * Example: + *

+     *          static class MyBeanTestData {
+     *              public static MyBean myBean(ValueProvider valueProvider) {
+     *                  // builds and returns your bean
+     *              }
+     *          }
+     *
+     *         ValueProvider vp = ValueProviderFactory.createRandomValueProvider();
+     *         vp.listOf(MyBeanTestData::myBean); // -> List[myBean_generated_1, myBean_generated_2]
+     *         vp.listOf(MyBeanTestData::myBean); // -> List[]
+     * 
+ *

+ * + * @param generator a generator {@link Function} to generate T given a {@link ValueProvider}. + * + * @return the generated {@link List}. + */ + public List listOf(Function generator) { + return listOf(intNumber(0, 5), generator); + } + + /** + * Generates a {@link List} of <T> (by means of {@code generator}). Containing exactly {@code numberOfElements} elements. + *

+ * Example: + *

+     *          static class MyBeanTestData {
+     *              public static MyBean myBean(ValueProvider valueProvider) {
+     *                  // builds and returns your bean
+     *              }
+     *          }
+     *
+     *         ValueProvider vp = ValueProviderFactory.createRandomValueProvider();
+     *         vp.listOf(4, MyBeanTestData::myBean); // -> List[myBean_generated_1, myBean_generated_2, myBean_generated_3, myBean_generated_4]
+     * 
+ *

+ * + * @param generator a generator {@link Function} to generate T given a {@link ValueProvider}. + * + * @return the generated {@link List}. + */ + public List listOf(int numberOfElements, Function generator) { + List generatedElements = new ArrayList<>(); + for (int i = 0; i < numberOfElements; i++) { + generatedElements.add(generator.apply((V) this)); + } + return generatedElements; + } + /** * Randomly draws true or false. * diff --git a/core/src/test/java/com/tngtech/valueprovider/ValueProviderTest.java b/core/src/test/java/com/tngtech/valueprovider/ValueProviderTest.java index 90f2340..f9a7cee 100644 --- a/core/src/test/java/com/tngtech/valueprovider/ValueProviderTest.java +++ b/core/src/test/java/com/tngtech/valueprovider/ValueProviderTest.java @@ -20,6 +20,9 @@ import java.util.stream.LongStream; import java.util.stream.Stream; +import lombok.Data; +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestFactory; @@ -32,6 +35,7 @@ import static com.tngtech.valueprovider.ValueProviderTest.MethodInvocation.assertDifferentResultAsFarAsPossible; import static com.tngtech.valueprovider.ValueProviderTest.MethodInvocation.assertEqualResult; import static com.tngtech.valueprovider.ValueProviderTest.MethodInvocation.invoke; +import static com.tngtech.valueprovider.ValueProviderTest.MyBeanTestData.myBeanContained; import static com.tngtech.valueprovider.ValueProviderTest.TestEnum.EIGHT; import static com.tngtech.valueprovider.ValueProviderTest.TestEnum.ELEVEN; import static com.tngtech.valueprovider.ValueProviderTest.TestEnum.FIVE; @@ -573,6 +577,44 @@ void someOf_should_return_duplicates_if_input_contains_duplicates() { .hasSize(valueList.size()); } + @Test + void listOf_should_return_a_sensible_number_of_elements() { + // given + ValueProvider random = withRandomValues(); + + // when + List myBeans = random.listOf(MyBeanTestData::myBean); + + // then + assertThat(myBeans).size().isLessThanOrEqualTo(5); // 5 is the default + } + + @Test + void nonEmptyListOf_should_return_at_least_one_element() { + // given + ValueProvider random = withRandomValues(); + + // when + List myBeans = random.nonEmptyListOf(MyBeanTestData::myBean); + + // then + assertThat(myBeans).size().isGreaterThanOrEqualTo(1); + assertThat(myBeans).size().isLessThanOrEqualTo(5); // 5 is the default + } + + @Test + void listOfContaining_should_return_the_provided_elements_plus_some_randomly_generated_elements() { + // given + ValueProvider random = withRandomValues(); + + // when + List myBeans = random.listOfContaining(MyBeanTestData::myBean, myBeanContained(1), myBeanContained(2), myBeanContained(3)); + + // then + assertThat(myBeans).size().isLessThanOrEqualTo(7); // 3 contained beans + max. 1 random 'spacing' bean between each + max. 1 random bean at the beginning/end + assertThat(myBeans).contains(myBeanContained(1), myBeanContained(2), myBeanContained(3)); + } + @Test void ipV6Address_should_return_valid_IPv6_address() throws UnknownHostException { ValueProvider random = withRandomValues(); @@ -673,4 +715,33 @@ private void assertBigDecimalNumberWithScale2(ValueProvider provider, Number min .isGreaterThanOrEqualTo(new BigDecimal(min.doubleValue() - 0.01)) .isLessThanOrEqualTo(new BigDecimal(max.doubleValue() + 0.01)); } + + static class MyBeanTestData { + public static MyBean myBean(ValueProvider valueProvider) { + return new MyBean("randomly generated"); + } + + public static MyBean myBeanContained(int counter) { + return new MyBean("contained" + counter); + } + } + + @Data + static class MyBean { + String value; + + public MyBean(String value) { + this.value = value; + } + + @Override + public boolean equals(Object o) { + return EqualsBuilder.reflectionEquals(this, o, true); + } + + @Override + public int hashCode() { + return HashCodeBuilder.reflectionHashCode(this, true); + } + } }