Giter VIP home page Giter VIP logo

fixture-monkey's Introduction

Fixture Monkey

Maven version Build GitHub license

Designed by SeongIn Hong

"Write once, Test anywhere"

Fixture Monkey is designed to easily generate controllable arbitrary instances. It allows you to reuse the same configurations of the instances in several tests.

You can write countless tests including edge cases, using just only one instance of the FixtureMonkey type. You can automatically generate instances of complex types and set fields with values from builders of the ArbitraryBuilder type. The well-defined builders can be reused in any test.

Each primitive type property is generated by Jqwik or kotest-property.

Requirements

  • JDK 1.8 or higher
  • Jqwik 1.7.3
  • Kotlin 1.8 or higher
  • kotest-property 5.6.2

Install

Gradle

Java

testImplementation("com.navercorp.fixturemonkey:fixture-monkey-starter:1.0.16")

Kotlin

testImplementation("com.navercorp.fixturemonkey:fixture-monkey-starter-kotlin:1.0.16")

Maven

Java

<dependency>
    <groupId>com.navercorp.fixturemonkey</groupId>
    <artifactId>fixture-monkey-starter</artifactId>
    <version>1.0.16</version>
    <scope>test</scope>
</dependency>

Kotlin

<dependency>
    <groupId>com.navercorp.fixturemonkey</groupId>
    <artifactId>fixture-monkey-starter-kotlin</artifactId>
    <version>1.0.16</version>
    <scope>test</scope>
</dependency>

Example

Add "lombok.anyConstructor.addConstructorProperties=true" in lombok.config

Java

@Value
public class Order {
    Long id;

    String orderNo;

    String productName;

    int quantity;

    long price;

    List<String> items;

    Instant orderedAt;
}

@Test
void sampleOrder() {
    // given
    FixtureMonkey sut = FixtureMonkey.builder()
            .objectIntrospector(ConstructorPropertiesArbitraryIntrospector.INSTANCE)
            .build();

    // when
    Order actual = sut.giveMeBuilder(Order.class)
            .set(javaGetter(Order::getOrderNo), "1")
            .set(javaGetter(Order::getProductName), "Line Sally")
            .minSize(javaGetter(Order::getItems), 1)
            .sample();

    // then
    then(actual.getOrderNo()).isEqualTo("1");
    then(actual.getProductName()).isEqualTo("Line Sally");
    then(actual.getItems()).hasSizeGreaterThanOrEqualTo(1);
}

Kotlin

data class Order (
    val id: Long,

    val orderNo: String,

    val productName: String,

    val quantity: Int,

    val price: Long,

    val items: List<String>,

    val orderedAt: Instant
)

@Test
fun sampleOrder() {
    // given
    val sut = FixtureMonkey.builder()
            .plugin(KotlinPlugin())
            .build()

    // when
    val actual = sut.giveMeBuilder<Order>()
            .setExp(Order::orderNo, "1")
            .setExp(Order::productName, "Line Sally")
            .minSizeExp(Order::items, 1)
            .sample()

    // then
    then(actual.orderNo).isEqualTo("1")
    then(actual.productName).isEqualTo("Line Sally")
    then(actual.items).hasSizeGreaterThanOrEqualTo(1)
}

Documentation

Plugins

  • FixtureMonkey Helper
    • IntelliJ plugin that makes it easier to use Fixture Monkey string expressions & Kotlin DSL

Contributors

Thanks to all contributors

More about Fixture Monkey

Articles

Welcome to write articles about Fixture Monkey!

License

Copyright 2021-present NAVER Corp.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
    http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

fixture-monkey's People

Contributors

acktsap avatar benelog avatar chanhyeong avatar doljae avatar doxxx93 avatar esfomeado avatar graceful-martin avatar hubtwork avatar iamzin avatar im-gnar avatar jbl428 avatar jinia91 avatar joecp17 avatar jwchung avatar jwp345 avatar kshired avatar mhyeon-lee avatar mollder avatar msugo1 avatar sandrawangyx avatar sangy515 avatar seongahjo avatar songkg7 avatar sookim1110 avatar this-is-spear avatar vincentj2 avatar voyager003 avatar wicksome avatar yn-jn-j avatar yunnote avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fixture-monkey's Issues

Unmappable character for encoding Cp1252

Describe the bug

The compilation fails on a system that us Cp1252 encoding due to the Korean comments.

Your environment

  • Fixture Monkey: 0.4.5
  • Java 17

Steps to reproduce

Compile the code using Cp1252 encoding.

Expected behaviour

Should compile successfully.

Actual behaviour

Compilation fails.

Comments causing the issue:

fixture-monkey\src\main\java\com\navercorp\fixturemonkey\arbitrary\ArbitraryTraverser.java:296

fixture-monkey\src\main\java\com\navercorp\fixturemonkey\arbitrary\DefaultContainerArbitraryNodeGenerator.java:141

fixture-monkey\src\main\java\com\navercorp\fixturemonkey\generator\ArrayBuilder.java:70

Set Value lazily

Set lazy value which is different when call set and call sample
New operation would determines value when call sample

As is

// given
ArbitraryBuilder<String> variable = fixture.giveMeBuilder(String.class);
ArbitraryBuilder<String> builder = fixture.giveMeBuilder(String.class)
    .set("$", variable.sample());
variable.set("test");

// when
String actual = builder.sample();

then(actual).isNotEqualTo("test"); 

To-be

// given
ArbitraryBuilder<String> variable = fixture.giveMeBuilder(String.class);
ArbitraryBuilder<String> builder = fixture.giveMeBuilder(String.class)
    .set("$", () -> variable.sample());
variable.set("test");

// when
String actual = builder.sample();

then(actual).isEqualTo("test");

It works even when sampled twice

// given
ArbitraryBuilder<String> variable = fixture.giveMeBuilder(String.class);
ArbitraryBuilder<String> builder = fixture.giveMeBuilder(String.class)
    .set("$", () -> variable.sample());
String expected = builder.sample();

// when
String actual = builder.sample();

then(actual).isEqualTo(expected);

Integration with Mockito

Please comment any features you had been impressed in Mockito, check it out if they could be supported by Fixture Monkey.
We'd like to make Fixture Monkey more friendly to Mockito users by improving fixture-monkey-mockito module

Cannot invoke "net.jqwik.api.Arbitrary.asGeneric()" because "this.arbitrary" is null

Describe the bug

Cannot invoke "net.jqwik.api.Arbitrary.asGeneric()" because "this.arbitrary" is null

Your environment

Java 17

FixtureMonkey - 0.4.10

Steps to reproduce

Run abstractTest().

https://github.com/esfomeado/fixture-monkey-bug/blob/master/src/test/java/com/example/bug/FixtureMonkeyTest.java

Expected behaviour

The test should pass.

Actual behaviour

I get this exception:

Cannot invoke "net.jqwik.api.Arbitrary.asGeneric()" because "this.arbitrary" is null

branch ์ „๋žต ๋…ผ์˜

  • ๋‹ค์Œ patch ๋ฒ„์ „์„ ์˜ฌ๋ฆฌ๊ธฐ ์œ„ํ•œ branch ์™€ ๊ณผ break change ๊ฐ€ ์žˆ์–ด ๋งˆ์ด๋„ˆ๋ฒ„์ „์„ ์˜ฌ๋ฆฌ๊ธฐ ์œ„ํ•œ branch ๊ฐ€ ๋ณ„๋„๋กœ ๊ด€๋ฆฌ๋  ํ•„์š”๊ฐ€ ์žˆ์„๊ฑฐ ๊ฐ™์Šต๋‹ˆ๋‹ค.
  • branch ์ „๋žต์„ ์–ด๋–ป๊ฒŒ ๊ฐ€์ ธ๊ฐ€๋ฉด ์ข‹์„์ง€ ์ •ํ•  ํ•„์š”๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

Figure out why generating empty container in key

#418

Map<Map<String, String>, String>> ์ฒ˜๋Ÿผ key ๊ฐ’์ด ์ปจํ…Œ์ด๋„ˆ์ธ ๊ฒฝ์šฐ ์ƒ์„ฑํ•  ๋•Œ ๋ฐ˜๋ณต์ ์œผ๋กœ size๊ฐ€ 0์ธ ๊ฒฝ์šฐ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.
size๊ฐ€ 0์ด ๋˜์–ด ์ค‘๋ณต๋œ ํ‚ค๊ฐ€ ๋ฐœ์ƒํ•˜์—ฌ ๊ฐ์ฒด ์ƒ์„ฑ์— ์‹คํŒจํ•˜๊ฒŒ ๋œ๋‹ค.
์›์ธ์„ ์กฐ์‚ฌํ•˜๊ณ  ์ •๋ฆฌํ•œ๋‹ค.

์ปดํŒŒ์ผ ์‹œ์  ํ‰๊ฐ€ readonly

๊ณ๋“ค์—ฌ์„œ ์‚ฌ์šฉ ์ค‘์ธ ๊ฐœ์ธ ํ”„๋กœ์ ํŠธ๊ฐ€ ์‹ค์ œ๋กœ ์ด ๊ณณ์—์„œ๋„ ๋„์›€์ด ๋˜์ง€ ์•Š์„๊นŒ... ๊ถ๊ธˆํ•ด์„œ ์ด์Šˆ๋ฅผ ๋‚จ๊ฒจ์š”.
์ œ์•ˆํ•˜๊ณ ์žํ•˜๋Š” ๋‚ด์šฉ์€ ์•„๋ž˜์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • Runtime ์‹œ์ ์—์„œ ํ‰๊ฐ€๋˜๋Š” read-only collection์„ ์ปดํŒŒ์ผ ์‹œ์ ์—์„œ ํ‰๊ฐ€๋˜๋Š” read-only collection์œผ๋กœ

์ปดํŒŒ์ผ ์‹œ์ ์—์„œ์˜ ํ‰๊ฐ€๊ฐ€ ์ƒ์‚ฐ์„ฑ์— ๋„์›€์„ ์ค€๋‹ค๋Š” ์˜๊ฒฌ์ž…๋‹ˆ๋‹ค.

๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

Generator for constructor in Java

ํ˜„์žฌ Java์—์„œ๋Š” ํด๋ž˜์Šค๋‚ด์— ์กด์žฌํ•˜๋Š” ํ•„๋“œ๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์กฐํ•ฉํ•˜์—ฌ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“œ๋Š” Generator๋“ค๋งŒ ์ง€์›ํ•˜๊ณ  ์žˆ๋‹ค.
์ƒ์„ฑ์ž๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ์ฒด๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐฉ๋ฒ•์€ ConstructorPropertiesArbitraryGenerator ๊ฐ€ ์žˆ์ง€๋งŒ,
์ƒ์„ฑ์ž ์œ„์— @ConstructorProperties๋ฅผ ๋ช…์‹œํ•ด์ฃผ์–ด์•ผ ํ•˜๊ณ  ํ•„๋“œ์— ์—†๋Š” ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์ž…๋ ฅ๋ฐ›์„ ๊ฒฝ์šฐ ์˜ˆ์™ธ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

Kotlin์—์„œ๋Š” PrimaryConstructorArbitraryGenerator๊ฐ€ ์ƒ์„ฑ์ž๋ฅผ ํ†ตํ•ด ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

์ƒ์„ฑ์ž ๊ธฐ๋ฐ˜์œผ๋กœ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๊ฒฝ์šฐ๋ฅผ ์ง€์›ํ•˜๊ธฐ ์œ„ํ•ด ์ƒˆ๋กœ์šด Generator๋ฅผ ๋งŒ๋“ ๋‹ค.


Currently, Java only supports generators which create objects by creating and combining fields existing in the class.
There is a 'ConstructorPropertiesArbitraryGenerator' to create an object using the constructor,
'@ConstructorProperties' must be specified above the constructor, and exception processing is required if parameters that are not in the field are input.

In Kotlin, the 'PrimaryConstructorArbitraryGenerator' may generate an object through the generator.

A new generator is created to support the creation of objects based on the constructor.

Generate nested class

Describe the feature you request

Generating nested class.

An example below.

class A {
    class B {
        String value;
    }
}

Could generate an instance of B

(Optional): Suggest A Solution

A concise description of your preferred solution.

Add register and select ArbitraryBuilder by name

์—ฌ๋Ÿฌ ์ข…๋ฅ˜์˜ ArbitraryBuilder ๋ฅผ ๋“ฑ๋กํ•˜๊ณ  ์„ ํƒํ•  ์ˆ˜ ์žˆ๋„๋ก ์ด๋ฆ„์„ ๋ถ€์—ฌํ•  ์ˆ˜ ์žˆ๋„๋ก ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

Add onManipulated

void onManipulated(BuilderManipulator manipulator)
ArbitraryBuilder์— ์—ฐ์‚ฐ์„ ์ ์šฉํ•˜๋ฉด onManipulated ๋ฉ”์†Œ๋“œ๊ฐ€ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค.

ArbitraryBuilder ์ƒ์„ฑ์‹œ onManipulated๋ฅผ manual๋กœ ์ž…๋ ฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
e.g. fixture.giveMeBuilder(Class<?> clazz, Consumer<BuilderManipulator> onManipulated)
onManipulated๋Š” ์ƒ์„ฑํ•  ๋•Œ๋งŒ ์ž…๋ ฅ๋ฐ›๊ณ , ๋ณ€๊ฒฝ์ด ๋ถˆ๊ฐ€๋Šฅํ•ด์•ผ ์˜ˆ์ธก ๊ฐ€๋Šฅํ•  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

์‚ฌ์šฉ ์˜ˆ์ œ

ํŠน์ • ํ•„๋“œ์— ์—ฐ์‚ฐ์„ ์ ์šฉํ•  ๊ฒฝ์šฐ ํ›„์† ์ฒ˜๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ•  ๋•Œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
e.g. DeliveryMethodType์ด Delivery์ผ ๋•Œ ํ•„๋“œ ์„ค์ • ๋“ฑ..
e.g. ๊ฒฐ์ œ ์ˆ˜๋‹จ์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์ง€๋Š” ํ•„๋“œ ๊ฐ’๋“ค ์„ค์ •
#31 ์˜ ๋Œ€์•ˆ์ด ๋  ์ˆ˜ ์žˆ์ง€ ์•Š์„๊นŒ ์‹ถ์Šต๋‹ˆ๋‹ค.

Support for interface

It seems that FixtureMonkey always returns null for an interface arbitrary builder.
Is there any chance to support interface arbitrary builder?

If it is hard to scan implementation candidates automatically, you can just let users to give the candidates.

If we have an interface I and it's implementations A, B and C, it would be great to support this kind of feature.
(Please consider my code as a kotlin-like pseudo code.)

interface I

class A : I
class B : I
class C : I

val fixtureMonkey: FixtureMonkey

@Test
fun generateI() {
    val sampled: I = fixtureMonkey.giveMeBuilder<I>()
        .sample() // return null or throw an exception
}

@Test
fun generateAOrB() {
    val builder: ArbitraryBuilder<I> = fixtureMonkey.giveMeBuilder<I>(A::class.java, B::class.java)

    val sampled1: I = builder.sample() // return an instance of A or B
    val sampled2: I = builder.sample() // return an instance of A or B

    then(sampled1).is {
        it is A || it is B
    }
    then(sampled2).is {
        it is A || it is B
    }
    then(sampled1).isNotEqualTo(sampled2)
}

Thanks!

Partial build while generating map or set

map ํ˜น์€ set๊ณผ ๊ฐ™์ด element์˜ ์ค‘๋ณต์„ ํ—ˆ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋‹ค.
์ž„์˜๋กœ ์ƒ์„ฑํ•œ element๊ฐ€ ์ค‘๋ณต๋˜๋Š” ๊ฒฝ์šฐ ์ƒ์„ฑํ•˜๋ ค๋Š” element ๊ฐœ์ˆ˜๋ณด๋‹ค ์ ๊ฒŒ ์ƒ์„ฑ๋  ์ˆ˜ ์žˆ๋‹ค.
์ง€๊ธˆ์€ filtering์„ ํ†ตํ•ด ๊ฐ์ฒด ์ „์ฒด๋ฅผ ๋งŒ์กฑํ•  ๋•Œ ๊นŒ์ง€ ๋ฐ˜๋ณต์ ์œผ๋กœ ์ƒ์„ฑํ•œ๋‹ค.
๋ถ€๋ถ„์ ์œผ๋กœ map ํ˜น์€ set๋งŒ ์ƒ์„ฑํ•˜๋„๋ก ๊ตฌํ˜„ํ•œ๋‹ค.

Incompatibility between BuilderArbitraryCustomizer and manipulating the instance

I have a class with a BuilderArbitraryCustomizer where I set some Arbitraries.
In my tests, I want to use giveMeBuilder(MyClass).set(fieldName_managed_by_arbitrary, myValue).sample() to get an instance with my wished values. This is the arbitrary I use:

.use(Arbitraries.strings().numeric().ofMaxLength(3)).in(MyClass.MyClassBuilder::myField);

Expected
Instance with my entered value

Current result
Instance with random value from the arbitrary

Add Unique operation

๋ฐฐ๊ฒฝ

Jqwik์—์„œ๋„ uniqueํ•œ ๊ฐ์ฒด ์ƒ์„ฑ์„ ์ง€์›ํ•˜์ง€๋งŒ ์ปฌ๋ ‰์…˜ ๋‚ด์—์„œ๋งŒ unique๊ฐ€ ๋ณด์žฅ๋œ๋‹ค.
์ž์‹ ๊ฐ์ฒด์˜ ์ปฌ๋ ‰์…˜์ด ๋ถ€๋ชจ ๊ฐ์ฒด ๋‚ด๋ถ€์— ์žˆ์„ ๊ฒฝ์šฐ์— jqwik์˜ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•ด์„œ unique๋ฅผ ๋ณด์žฅํ•  ์ˆ˜ ์žˆ์ง€๋งŒ ๋ฒˆ๊ฑฐ๋กญ๋‹ค.
Fixture Monkey์—์„œ ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ณ ์ž ํ•œ๋‹ค.

์™„๋ฃŒ์กฐ๊ฑด

๋™์ผํ•œ ArbitraryBuilder์—์„œ Unique ์—ฐ์‚ฐ์„ ์ ์šฉํ•˜์—ฌ ์ƒ์„ฑํ•œ ๊ฐ์ฒด๋Š” ์œ ์ผํ•จ์„ ๋ณด์žฅํ•ด์•ผ ํ•œ๋‹ค.
Unique ์—ฐ์‚ฐ์€ ์ƒ์„ฑํ•  ๊ฐ์ฒด ๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ์ƒ์„ฑํ•˜๋Š” ๊ฐ์ฒด ๋‚ด๋ถ€์˜ ๊ฐ์ฒด์—๋„ ํ‘œํ˜„์‹์„ ํ†ตํ•ด ์ ์šฉ์ด ๊ฐ€๋Šฅํ•ด์•ผ ํ•œ๋‹ค.

refactor applying ArbitraryCustomizer not only for type but also for more conditions

As-is

๊ณตํ†ต์œผ๋กœ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ArbitraryCustomizer๊ฐ€ ์žˆ์–ด๋„ ํƒ€์ž…๋งˆ๋‹ค ๋“ฑ๋ก์„ ํ•ด์ฃผ์–ด์•ผ ํ•œ๋‹ค.
ex. ApplicationEvent ๊ตฌํ˜„์ฒด

To-be

๊ณตํ†ต์œผ๋กœ ์ ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ArbitraryCustomizer๋Š” ํ•œ ๋ฒˆ๋งŒ ๋“ฑ๋กํ•˜๋ฉด ์กฐ๊ฑด์— ํ•ด๋‹นํ•˜๋Š” ํƒ€์ž… ๋ชจ๋‘ ์ ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.


As-is

Even if there is an 'ArbitraryCustomizer' that can be commonly applied, registration must be made for each type.

To-be

'ArbitraryCustomizer', which can be commonly applied, can be applied to all types that meet the conditions by registering once.

Add Recorder in fixture-monkey-engine

  • ArbitraryBuilder ์—์„œ build ์‹œ ๋ฐ˜ํ™˜ํ•˜๋Š” Arbitrary ์˜ ๊ตฌ์กฐ๋ฅผ ๊ฐœ์„ ํ•ฉ๋‹ˆ๋‹ค.
  • ArbitraryValue ๊ฐ€ Validation ๊ณผ ConstratinViolationException ๋“ฑ์˜ ์—ญํ• ์„ ์˜จ์ „ํžˆ ํ•˜์ง€ ๋ชปํ•ด์„œ ๊ตฌ์กฐ๋ฅผ ๋‹ค์‹œ ์žก์„ ํ•„์š”๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ArbitraryValue ๋Š” ์ œ๊ฑฐํ•˜๊ณ  ArbitraryBuilder ์—์„œ Validation Filter ๋ฅผ ์ฃผ์ž…ํ•˜๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
  • TooManyFilter... Exception ๊ณผ ConstratinViolationException ์— ๋”ฐ๋ฅธ ๊ธฐ๋ก๊ณผ ๋กœ๊น…์€ ํ…Œ์ŠคํŠธ ์‹คํŒจ callback ์„ ๊ตฌํ˜„ํ•ด์„œ ๊ธฐ๋กํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.
  • ๊ธฐ๋ก์„ ์œ„ํ•œ Recorder ์„ค๊ณ„๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

#39 (comment)

image


  • ArbitraryValue ๊ฐ€ ๊ณต๊ฐœ ํด๋ž˜์Šค๊ฐ€ ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ตฌ์กฐ ๋ณ€๊ฒฝ์ด ์‚ฌ์šฉ์ž์—๊ฒŒ ์˜ํ–ฅ์„ ๋ผ์น˜์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค.

When putting a value through reflect, a permission denied Exception is thrown.

Hello, I am applying fixture monkey and strangely it works fine locally, but I get a complie error on the linux server.

error while writing /home1/irteam/.../P...Test$...Test1$...Test$1.class (Permission denied)

I tried taking a stacktrace, but I can't find any detailed information.
It seems that Permission denied occurs when you put a value through Relection.

fixture.giveMeBuilder(Attachment::class.java)
                .setExp(
                    Attachment::fileType,
                    Arbitraries.of(FileType.FILE, FileType.VIDEO, FileType.IMAGE)
                )
                .sampleList(30)
class Attachment(
    val fileType: FileType,  
) 

The fixture monkey version uses a snapshot version as shown below.

testImplementation("com.navercorp.fixturemonkey.snapshot:fixture-monkey-starter:0.4.0-dd71410")
testImplementation("com.navercorp.fixturemonkey.snapshot:fixture-monkey-kotlin:0.4.0-dd71410")
testImplementation("com.navercorp.fixturemonkey.snapshot:fixture-monkey-mockito:0.4.0-dd71410")
testImplementation("com.navercorp.fixturemonkey.snapshot:fixture-monkey-jackson:0.4.0-dd71410")

Do you have any solution?

===========================================================================
์•ˆ๋…•ํ•˜์„ธ์š” fixture monkey ๋ฅผ ์ ์šฉ์ค‘์ธ๋ฐ ์ด์ƒํ•˜๊ฒŒ local ์—์„œ๋Š” ๊ดœ์ฐฎ์€๋ฐ, linux ์„œ๋ฒ„์œ„์—์„œ complie ์—๋Ÿฌ๊ฐ€ ๋‚ฉ๋‹ˆ๋‹ค.

error while writing /home1/irteam/.../P...Test$...ํ…Œ์ŠคํŠธ1$... ํ…Œ์ŠคํŠธํ•œ๋‹ค$1.class (Permission denied)

stacktrace ๋ฅผ ์ฐ์–ด๋ด๋„ ์ž์„ธํ•œ ์ •๋ณด๊ฐ€ ์•ˆ๋‚˜์˜ค๋Š”๋ฐ์š”.
Relection ์„ ํ†ตํ•ด ๊ฐ’์„ ๋„ฃ์–ด์ค„ ๋•Œ Permission denied ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์œผ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค.

fixture.giveMeBuilder(Attachment::class.java)
                .setExp(
                    Attachment::fileType,
                    Arbitraries.of(FileType.FILE, FileType.VIDEO, FileType.IMAGE)
                )
                .sampleList(30)
class Attachment(
    val fileType: FileType,  
) 

fixture monkey ๋ฒ„์ „์€ ์•„๋ž˜์™€ ๊ฐ™์ด ์Šค๋ƒ…์ƒท ๋ฒ„์ „์„ ์“ฐ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

		testImplementation("com.navercorp.fixturemonkey.snapshot:fixture-monkey-starter:0.4.0-dd71410")
		testImplementation("com.navercorp.fixturemonkey.snapshot:fixture-monkey-kotlin:0.4.0-dd71410")
		testImplementation("com.navercorp.fixturemonkey.snapshot:fixture-monkey-mockito:0.4.0-dd71410")
		testImplementation("com.navercorp.fixturemonkey.snapshot:fixture-monkey-jackson:0.4.0-dd71410")

ํ˜น์‹œ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์ด ์žˆ์„๊นŒ์š”?

Add Expression strict mode

  • Expression ๊ฐ’์ด ์‹ค์ œ ๋ชจ๋ธ์— ๋งคํ•‘๋˜์ง€ ์•Š๋Š” ๋‹ค๋ฉด ์—๋Ÿฌ๋ฅผ ๋‚ด๋Š” strict mode ๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
  • ๋ชจ๋ธ์˜ ํ•„๋“œ๋ช…์ด๋‚˜ ์ŠคํŽ™์ด ๋ณ€๊ฒฝ๋˜์—ˆ์„ ๋•Œ expression ๋„ ๊ฐ™์ด ์ˆ˜์ •ํ•ด์ค˜์•ผ ํ•˜๋Š”๋ฐ ๋†“์น ๊ฒฝ์šฐ ์ด๋ฅผ ๊ฒ€์ฆํ•˜๋Š” ์šฉ๋„๋กœ ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

size in apply not working when registering size manipulation

ํ…Œ์ŠคํŠธ

@Property
void applySizeWhenRegisteredWithSize() {
// given
LabMonkey sut = LabMonkey.labMonkeyBuilder()
	.register(
		ListStringObject.class,
		fixture -> fixture.giveMeBuilder(ListStringObject.class).size("values", 5)
	)
	.build();

// when
List<String> actual = sut.giveMeBuilder(ListStringObject.class)
	.apply((it, builder) -> builder.size("values", 10))
	.sample()
	.getValues();

then(actual).hasSize(10);
}

Randomly apply manipulator when applying manipulator with limit

ArbitraryNodes found by expression has fixed ordering.
So applying manipulator with parameter limit, which parameter used as limiting the number of applied ArbitraryNode, always returns same applied ArbitraryNode.
It is not an intuitive result.

In short, this issue is to shuffle all ArbitraryNodes found by ArbitraryExpression.


์ ์šฉ ํšŒ์ˆ˜๋ฅผ ์ž…๋ ฅ๋ฐ›๋Š” ์—ฐ์‚ฐ (limit ์„ ์ž…๋ ฅ๋ฐ›๋Š” ์—ฐ์‚ฐ)์—์„œ ์กฐํšŒํ•œ ๋…ธ๋“œ ์ˆœ์„œ๋Œ€๋กœ ์ ์šฉํ•˜๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ ๋žœ๋คํ•˜๊ฒŒ ์ ์šฉํ•˜๋„๋ก ์ˆ˜์ •ํ•ฉ๋‹ˆ๋‹ค.

Test code intermittently fails when using JavaxValidationPlugin

Describe the bug

Test code intermittently fails when using JavaxValidationPlugin

Your environment

  • version of Fixture Monkey: 0.4.11
  • version of Java/Kotlin: Kotlin 1.7.22

Steps to reproduce

Repository that reproduced the problem

I wrote 2 test codes. This test code does the same logic. Only the presence or absence of LabMonkey's JavaxValidationPlugin that creates fixtures is different.

@Test
fun getSavedQuestionnaire() {
    val uri = "/test"
    val sut: LabMonkey = LabMonkey.labMonkeyBuilder()
        .defaultNullInjectGenerator { DefaultNullInjectGenerator.NOT_NULL_INJECT }
        .plugin(KotlinPlugin())
        .plugin(JacksonPlugin())
        .build()
    val fixture = sut.giveMeOne<TestDataClass>()
    println(fixture.toString())
    every { testService.test() } returns fixture

    mockMvc
        .perform(MockMvcRequestBuilders.get(uri))
        .andExpect(MockMvcResultMatchers.status().isOk)
        .andExpect(MockMvcResultMatchers.jsonPath("$.id").value(fixture.id))
        .andExpect(MockMvcResultMatchers.jsonPath("$.name").value(fixture.name))
        .andExpect(MockMvcResultMatchers.jsonPath("$.name2").value(fixture.name2))
        .andExpect(MockMvcResultMatchers.jsonPath("$.name3").value(fixture.name3))
        .andDo(MockMvcResultHandlers.print())
}

// Fails intermittently
@Test
fun getSavedQuestionnaireWithJavaxPlugin() {
    val uri = "/test"
    val sut: LabMonkey = LabMonkey.labMonkeyBuilder()
        .defaultNullInjectGenerator { DefaultNullInjectGenerator.NOT_NULL_INJECT }
        .plugin(KotlinPlugin())
        .plugin(JacksonPlugin())
        .plugin(JavaxValidationPlugin())
        .build()
    val fixture = sut.giveMeOne<TestDataClass>()
    println(fixture.toString())
    every { testService.test() } returns fixture

    mockMvc
        .perform(MockMvcRequestBuilders.get(uri))
        .andExpect(MockMvcResultMatchers.status().isOk)
        .andExpect(MockMvcResultMatchers.jsonPath("$.id").value(fixture.id))
        .andExpect(MockMvcResultMatchers.jsonPath("$.name").value(fixture.name))
        .andExpect(MockMvcResultMatchers.jsonPath("$.name2").value(fixture.name2))
        .andExpect(MockMvcResultMatchers.jsonPath("$.name3").value(fixture.name3))
        .andDo(MockMvcResultHandlers.print())
}

Expected behaviour

JavaxValidation plug-in was added, but there is no part where javax.validation-related logic is used in fixture.
Therefore, I expect the same result, i.e. pass both test cases.

Actual behaviour

Test cases using JavaxValidationPlugin fail intermittently.

Additional info

It could be a bug in Spring and related tools, or my lack of proficiency in using it
Not exactly accurate, but the output of the path seems to be a Unicode-related issue.

Cannot set field if class doesn't contain same fields

Describe the bug

When setting a field sometimes it fails.

It seems that if the class generated that will be replaced contains fields that the other class doesn't contain it fails.

Your environment

Java 17 - FixtureMonkey - 0.4.9

Steps to reproduce

Run abstractTest() multiple times until it fails

https://github.com/esfomeado/fixture-monkey-bug/blob/master/src/test/java/com/example/bug/FixtureMonkeyTest.java

Expected behaviour

Should work all the times and set the field correctly.

Actual behaviour

The field is not set sometimes.

Can not set java.lang.String field com.example.bug.Peter.age to com.example.bug.John
java.lang.IllegalArgumentException: Can not set java.lang.String field com.example.bug.Peter.age to com.example.bug.John
	at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
	at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
	at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:58)
	at java.base/jdk.internal.reflect.UnsafeObjectFieldAccessorImpl.get(UnsafeObjectFieldAccessorImpl.java:36)

EnumMap isn't generated if the number of enum values is less than container min size

If the container size is bigger then the number of enum values it will fail to generate the EnumMap.

This is a special case that should ignore the min container size.

Sample project:
https://github.com/esfomeado/monkey-enum-bug

Two tests are provided:

void testWillAlwaysFail() - It will fail always since the container size is bigger than the number of enum values.

void testSometimesFail() - It will only fail sometimes since sometimes the container size is the same as the number of enum values.

Is there a way to create a Kotlin data class where all properties have non-nullable values?

Describe your question

Is there a way to create a Kotlin data class where all properties have non-nullable values?

For example...

data class A(
    val id:Long?,
    val name:String?,
    val inner: B?
)

data class B(
    val id:Long?,
    val name:String?,
    val inner: C?
)

data class C(
    val id:Long?,
    val name:String?,
)

When I have the class structure as above and create a fixture of class A with LabMonkey, I want to make all properties of class A non-nullable, and properties of its subclass(in this case, B & C) not only it's properties, but also itself too.

Is there any combination of settings that I can use in a situation like the one above?

FYI, the configuration below seems not work ๐Ÿ˜ญ

val sut = LabMonkey.labMonkeyBuilder()
    .defaultNotNull(true)
    .plugin(KotlinPlugin())
    .build()

Add defaultNotEmpty

It would be cool to have a option to always generate the lists with elements similar to how defaultNotNull works but in this case for always generating values on lists.

Add Caching

์บ์‹ฑ์„ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.

๋™์ผํ•œ ํ…Œ์ŠคํŠธ ๋‚ด์—์„œ ๊ฐ™์€ ๊ฐ์ฒด๋ฅผ ์—ฌ๋Ÿฌ ๋ฒˆ ์ƒ์„ฑํ•˜๋Š” ๊ฒฝ์šฐ๋Š” ๋“œ๋ฌธ ๊ฒฝ์šฐ์ด๊ธฐ ๋•Œ๋ฌธ์—,
์บ์‹ฑ์„ ์ ์šฉํ•ด๋„ ํฐ ์„ฑ๋Šฅ ์ฐจ์ด๋Š” ์—†์„ ๊ฒƒ์œผ๋กœ ์˜ˆ์ƒํ•˜์ง€๋งŒ ๊ฐ™์€ ๋™์ž‘์€ ํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ๋””๋ฒ„๊น…์ด ํŽธํ•ด์งˆ ๊ฒƒ ๊ฐ™๋‹ค.
์ž‘์—… ์šฐ์„ ์ˆœ์œ„๋Š” ๋†’์ง€ ์•Š๋‹ค.

Build

  1. decompose์ธ ๊ฒฝ์šฐ ์บ์‹œ๋ฅผ ์ ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค.
  2. build() ์—์„œ tree๋ฅผ ์ด๋ฏธ ์ƒ์„ฑํ•œ ๊ฒฝ์šฐ ์บ์‹ฑํ•˜์—ฌ ์‚ฌ์šฉํ•˜๋„๋ก ์ˆ˜์ •ํ•œ๋‹ค.
  3. traverseํ•  ๋•Œ ์ž…๋ ฅํ•œ ํƒ€์ž…์ด ์ด๋ฏธ ๊ฐ€์ƒ ํด๋ž˜์Šค ํŠธ๋ฆฌ๋ฅผ ์ƒ์„ฑํ–ˆ์œผ๋ฉด ์ƒ์„ฑํ•˜์ง€ ์•Š๊ณ  ์บ์‹ฑํ•œ ๊ฒฐ๊ณผ๋ฅผ ์‚ฌ์šฉ
    3.1. ์—ฐ์‚ฐ ์ ์šฉํ•˜๋Š” ๊ฒฝ์šฐ๋ฅผ Traverser์—์„œ ์‹๋ณ„ํ•ด์•ผ ํ•œ๋‹ค. (์ฐธ์กฐ ๋จธํดํŠธ๋ฆฌ, ๋ธ”๋ฃธํ•„ํ„ฐ?)
    3.2. Traverser๋Š” ๊ฐ™์€ Fixture Monkey ์ธ์Šคํ„ด์Šค์—์„œ ์ƒ์„ฑํ•œ ArbitraryBuilder์—์„œ ๊ณต์œ ํ•œ๋‹ค.

3๋ฒˆ์˜ ๊ฒฝ์šฐ, ๋ฆฌ์ŠคํŠธ๋ฅผ ํ•„๋“œ๋กœ ๊ฐ€์ง€๊ณ  ์žˆ๊ณ , ๋ฆฌ์ŠคํŠธ์˜ ํƒ€์ž…์ด ๋งŽ์€ ํ•„๋“œ๋ฅผ ๊ฐ€์งˆ ๊ฒฝ์šฐ ์œ ์˜๋ฏธํ•œ ์„ฑ๋Šฅ ์ฐจ์ด๋ฅผ ๋งŒ๋“ค์–ด๋‚ผ ์ˆ˜ ์žˆ์Œ. (e.g. ํ”ผ๋ณด๋‚˜์น˜ DP)
3๋ฒˆ์„ ํ•ด๊ฒฐํ•  ๋•Œ, ์–‘๋ฐฉํ–ฅ ์ฐธ์กฐ ๋ฌธ์ œ๋„ ํ•ด๊ฒฐํ•  ๊ฒƒ์ด๋ผ ์˜ˆ์ƒ. ํ•ด๊ฒฐ ๋ฐฉ์‹์€ ์ถ”ํ›„ ๊ณ ๋ฏผ์ด ํ•„์š”.

Sample

  1. fixed()๋ฅผ ์ ์šฉํ•˜๊ณ  isDirty()๊ฐ€ ์•„๋‹Œ ๊ฒฝ์šฐ, ํ•ญ์ƒ ๊ฐ™์€ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋ฏ€๋กœ sample()์ด ์‹คํ–‰๋  ๋•Œ root์˜ lazyValue ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

์ฃผ์˜ ํ•ด์•ผํ•  ์ 

  1. Lazy Evaluation

Upgrade jqwik 1.6.0 version

  • jqwik 1.6.0 version ์œผ๋กœ upgrade ํ•ฉ๋‹ˆ๋‹ค.
  • jqwik ์˜ braking change ๋ฅผ ๋Œ€์‘ํ•ฉ๋‹ˆ๋‹ค.
  • #34 #35 ๊ฐ€ ์„ ํ–‰๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Sample test in README seems not to be working

Describe your question

Looks like an interesting library. I wanted to try it out, but the sample test in the README did not work for me:

    @Test
    void sampleOrder() {
        // given
        LabMonkey sut = LabMonkey.labMonkeyBuilder()
                .objectIntrospector(ConstructorPropertiesArbitraryIntrospector.INSTANCE)
                .build();

        // when
        Order actual = sut.giveMeBuilder(Order.class)
                .set("orderNo", "1")
                .set("productName", "Line Sally")
                .minSize("items", 1)
                .sample();

        System.out.println(actual);
       // produces:
       // Order(id=null, orderNo=null, productName=null, quantity=0, price=0, items=null, orderedAt=null)

        // then
        then(actual.getOrderNo()).isEqualTo("1");
        then(actual.getProductName()).isEqualTo("Line Sally");
        then(actual.getItems()).hasSizeGreaterThanOrEqualTo(1);
    }

Dependency:

testImplementation 'com.navercorp.fixturemonkey:fixture-monkey-starter:0.4.10'

Cannot set field

Describe the bug

When setting a field where the parent is an abstract class and the parent of the parent is also an abstract class it throws an error saying No resolved property is found.

Your environment

Java 17

FixtureMonkey - 0.4.10

Steps to reproduce

Run abstractTest().

https://github.com/esfomeado/fixture-monkey-bug/blob/master/src/test/java/com/example/bug/FixtureMonkeyTest.java

Expected behaviour

Should set the field correctly.

Actual behaviour

Throws error message saying No resolved property is found.

Add Map Expression

Map ํƒ€์ž…์„ ์ œ์–ดํ•  ์ˆ˜ ์žˆ๋Š” Expression ์„ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.

Display what field failed to generate

It would be useful to know which field failed to generate instead of a generic message since sometimes isn't straightforward to see which field failed to generate and why.

Add PR Builder with various Java Version

์•„๋ž˜์™€ ๊ฐ™์ด ๋‹ค์–‘ํ•œ Java Version ์œผ๋กœ ๋ฌธ์ œ๊ฐ€ ์—†๋Š”์ง€ PR Builder ๋ฅผ ๋Œ๋ฆด ์ˆ˜ ์žˆ์œผ๋ฉด ์ข‹์„๊ฑฐ ๊ฐ™์Šต๋‹ˆ๋‹ค.

แ„‰แ…ณแ„แ…ณแ„…แ…ตแ†ซแ„‰แ…ฃแ†บ 2021-10-03 แ„‹แ…ฉแ„Œแ…ฅแ†ซ 12 45 33

JacksonArbitraryIntrospector used instead of FieldReflectionArbitraryIntrospector

Describe the bug

If I set a field to use the JacksonArbitraryIntrospector all the field fields will now use the JacksonArbitraryIntrospector instead the default one.

Your environment

Java 17

FixtureMonkey - 0.4.10

Steps to reproduce

Run invalidIntrospector on https://github.com/esfomeado/fixture-monkey-bug/blob/f5ca2c55e543a04cab279245fddfe6b946f59e5a/src/test/java/com/example/bug/FixtureMonkeyTest.java

Expected behaviour

Should generate the class correctly.

Actual behaviour

Class not generated.

Cannot construct instance of com.example.bug.Condition (no Creators, like default constructor, exist): abstract types either need to be mapped to concrete types, have custom deserializer, or contain additional type information

Generate anonymous object

Describe the feature you request

Generate an anonymous object from interface or abstract class.

(Optional): Suggest A Solution

Have to find out if it could be a Java feature or just only Kotlin feature.

Abstract class

Have to research more

Interface

Generate object by using dynamic proxy.

NullPointerException when using IterableSpec any()

Bug Report

@Test
void any() {
    // given
    FixtureMonkey fixture = FixtureMonkey.create();
    ExpressionSpec spec = new ExpressionSpec()
        .list("items", it -> {
            it.ofMinSize(2);
            it.any("set");
        }
    );
    
    // when
    Order actual = fixture.giveMeBuilder(Order.class)
        .spec(spec)
        .sample();
    
    then(actual.getItems()).anyMatch(it -> it.equals("set"));
}

it.any()๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์ „์— ์‚ฌ์ด์ฆˆ๋ฅผ ์ง€์ •ํ•˜์ง€ ์•Š๊ฑฐ๋‚˜ it.ofMinSize()๋กœ min size๋งŒ ์„ค์ •(ํ˜น์€ max size๋งŒ ์„ค์ •)ํ•˜๋ฉด NullPointerException์ด ๋ฐœ์ƒํ•œ๋‹ค.

IterableSpec์„ ๊ตฌํ˜„ํ•œ DefaultIterableSpec์—์„œ any()๋ฅผ ์ ์šฉํ•  ๋…ธ๋“œ์˜ ๊ฐœ์ˆ˜ limit์„ ๊ณ„์‚ฐํ•  ๋•Œ

Arbitraries.longs().between(this.minSize, this.maxSize).sample();

์œ„ ์ฝ”๋“œ์ฒ˜๋Ÿผ minSize์™€ maxSize๋ฅผ ์ด์šฉํ•˜๋Š”๋ฐ, minSize ๋˜๋Š” maxSize๊ฐ€ ๋ฏธ๋ฆฌ ์„ค์ •๋˜์ง€ ์•Š์•„์„œ NullPointerException์ด ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์œผ๋กœ ๋ณด์ธ๋‹ค.

Refactor container builder for scalability

Container๋ฅผ ์กฐํ•ฉํ•˜๋Š” Builder๋ฅผ ์‚ฌ์šฉ์ž๊ฐ€ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌ์กฐ๋ฅผ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.

Generator on kotlin secondary constructor

secondary constructor์— ๋Œ€ํ•œ generator

ํ•œ๊ฐœ๋งŒ ์žˆ์œผ๋ฉด ๊ทธ๊ฑธ ์‚ฌ์šฉํ•˜๊ณ  ์—ฌ๋Ÿฌ๊ฐœ๊ฐ€ ์žˆ์„ ๊ฒฝ์šฐ annotation์„ ์‚ฌ์šฉํ•ด์„œ mark

data class A(val value: Int) {
     constructor() : this(0) {
         // do something
     }

     @SecondaryConstructor // ์ด๋Ÿฐ ์‹์œผ๋กœ
     constructor(val value: String) : this(value.toInt()) {
         // do something
     }

}

Extending generic class results in stack overflow

Describe the bug

I've been playing around with some of the examples and ran into an issue extending a generic class.
Creating an instance of the subclass results in a stack overflow error.
Maybe I am missing some setup for this to work. If so, please let me know.

Your environment

  • version of Fixture Monkey: 0.4.11
  • version of Java: 17

Steps to reproduce

class Foo<T> {
    T foo;
}

class Bar extends Foo<String> {
    Double bar;
}

@Test
void example() {
    FixtureMonkey sut = FixtureMonkey.builder()
            .defaultGenerator(BeanArbitraryGenerator.INSTANCE)
            .build();

    Bar actual = sut.giveMeOne(Bar.class);

    assertThat(actual).isNotNull();
}

Expected behaviour

Return an instance of the class

Actual behaviour

java.lang.StackOverflowError
	at java.base/java.util.ArrayList.grow(ArrayList.java:244)
	at java.base/java.util.ArrayList.add(ArrayList.java:454)
	at java.base/java.util.ArrayList.add(ArrayList.java:467)
	at java.base/java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
	at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179)
	at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1625)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
	at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682)
	at org.junit.platform.commons.util.ReflectionUtils.findAllFieldsInHierarchy(ReflectionUtils.java:1204)
	at org.junit.platform.commons.util.ReflectionUtils.findFields(ReflectionUtils.java:1190)
	at com.navercorp.fixturemonkey.TypeSupports.extractFields(TypeSupports.java:92)
	at com.navercorp.fixturemonkey.arbitrary.ArbitraryTraverser.doTraverse(ArbitraryTraverser.java:111)
	at com.navercorp.fixturemonkey.arbitrary.ArbitraryTraverser.doTraverse(ArbitraryTraverser.java:140)
	at com.navercorp.fixturemonkey.arbitrary.ArbitraryTraverser.doTraverse(ArbitraryTraverser.java:140)

Refactor decompose

As-is

ํ˜„์žฌ๋Š” decompose ์—ฐ์‚ฐ์„ ์‹คํ–‰ํ•˜๋ฉด ํŠธ๋ฆฌ์— ๊ฐ’์„ ์„ค์ •ํ•˜๊ณ , traverser์—์„œ ๊ฐ’์„ arbitrary๋กœ ๋ณ€ํ™˜ํ•ด์„œ ๋…ธ๋“œ์— ํ• ๋‹นํ•œ๋‹ค.
๋…ธ๋“œ๋Š” Supplier๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๊ณ  Lazyํ•˜๊ฒŒ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ์‹ค์ œ Traverser์—์„œ ๊ฐ’์„ ์š”์ฒญํ•  ๋•Œ ๊ฐ’์„ ์ƒ์„ฑํ•œ๋‹ค.
๊ฐ’์„ ์ƒ์„ฑํ•˜๋Š” ์‹œ์ ์ด ์˜ˆ์ธก ๋ถˆ๊ฐ€ํ•˜๊ณ , ๊ธฐ์กด ์—ฐ์‚ฐ๊ณผ ๋‹ค๋ฅด๊ฒŒ ๋™์ž‘ํ•˜์—ฌ ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ๋‹ค.

To-be

๊ฐœ์„ ์•ˆ์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.
decompose ์—ฐ์‚ฐ์„ ๊ธฐ์กด Set ์—ฐ์‚ฐ์„ ํ™œ์šฉํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๋ณ€๊ฒฝํ•œ๋‹ค.
๊ฐ’์„ ์„ค์ •ํ•  ๋•Œ(Set) ๊ฐ’์„ ๋ถ„ํ•ดํ•˜์—ฌ ํ•˜์œ„ ๋…ธ๋“œ์— ์„ค์ •ํ•œ๋‹ค.

์ด๋ ‡๊ฒŒ ๋ณ€๊ฒฝํ•˜๋ฉด ์ƒ์œ„ ํ•„๋“œ ๊ฐ’์„ ๋ณ€๊ฒฝํ•œ ํ›„์— ํ•˜์œ„ ํ•„๋“œ ๊ฐ’์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค.
e.g.

class Person{
    Company company;
}

class Company{
    String name;
    String description;
}

ํ˜„์žฌ๋Š” ์•„๋ž˜์™€ ๊ฐ™์€ ์—ฐ์‚ฐ์„ ์‹คํ–‰ํ•˜๋ฉด 2๋ฒˆ ์งธ ์—ฐ์‚ฐ์ด ์ ์šฉ๋˜์ง€ ์•Š๋Š”๋‹ค.
์ด๋Š” ์ผ๋ฐ˜์ ์œผ๋กœ ์˜ˆ์ธกํ•œ ๋™์ž‘ ๊ฒฐ๊ณผ์™€ ๋‹ค๋ฅด๋‹ค.

  1. .set("company", new Company("NAVER"))
  2. .set("company.name", "NCloud")

์˜ˆ์ƒํ•˜๋Š” ๋ฌธ์ œ์ 

  • ์ด์ „ ๊ตฌ์กฐ์™€ ๋‹ค๋ฅด๊ฒŒ sampleํ•  ๋•Œ ๋งˆ๋‹ค ๋™์ ์œผ๋กœ ๊ฐ’์„ ์ง€์ •ํ•  ์ˆ˜ ์—†๋‹ค.

๊ฐœ์„ ์•ˆ์„ ์ ์šฉํ•˜๋ฉด ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ณ , ๋ณต์žก๋„๋ฅผ ํฌ๊ฒŒ ์ค„์ผ ์ˆ˜ ์žˆ๋‹ค.

Remove org.junit.platform.x dependencies

Remove org.junit.platform.xx dependencies.
Replace the utility code of org.junit.platform.xx to use internal code.

For example, removing the BeanArbitraryGenerator ReflectionUtils usage code and replace it with a utility code that does the same thing inside.


org.junit.platform.xx ์˜ ์œ ํ‹ธ๋ฆฌํ‹ฐ ์‚ฌ์šฉํ•œ ์ฝ”๋“œ๋“ค์„ ๋‚ด๋ถ€ ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ๋Œ€์ฒดํ•ฉ๋‹ˆ๋‹ค.
์ฝ”๋“œ์—์„œ org.junit.platform.xx ์˜์กด ์ฝ”๋“œ๋“ค์„ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด BeanArbitraryGenerator ์˜ ReflectionUtils ์‚ฌ์šฉ ์ฝ”๋“œ๋ฅผ ์ œ๊ฑฐํ•˜๊ณ  ๋™์ผํ•œ ๋™์ž‘์„ ํ•˜๋Š” ์œ ํ‹ธ๋ฆฌํ‹ฐ ์ฝ”๋“œ๋ฅผ ๋‚ด๋ถ€์— ์ž‘์„ฑํ•ด์„œ ๋Œ€์ฒดํ•ฉ๋‹ˆ๋‹ค.

AutoAssign ์„ค์ •

PR ์ด ์˜ฌ๋ผ์˜ค๋ฉด AutoAssign ์„ ๊ฑธ์–ด๋„ ์ข‹์„๊ฑฐ ๊ฐ™์Šต๋‹ˆ๋‹ค.

adjust specAny manipulators order

์—ฐ์‚ฐ์€ ์„ธ ๊ฐ€์ง€ ์ข…๋ฅ˜๊ฐ€ ์žˆ๋‹ค.
MetadataManipulator -> OrderedArbitraryManipulator -> PostArbitraryManipulators ์˜ ์ˆœ์„œ๋กœ ์‹คํ–‰์ด ๋œ๋‹ค.

MetadataManipulator ๋Š” ์šฐ์„ ์ˆœ์œ„์— ๋”ฐ๋ผ ์ •๋ ฌํ•ด์„œ ์‹คํ–‰ํ•œ๋‹ค.
OrderedArbitraryManipulator, PostArbitraryManipulators ๋Š” ์—ฐ์‚ฐ์„ ์„ ์–ธํ•œ ์ˆœ์„œ๋Œ€๋กœ ์‹คํ–‰ํ•œ๋‹ค.

specAny๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์„ธ ์ข…๋ฅ˜์˜ ์—ฐ์‚ฐ์— ์„ค์ •ํ•œ ์—ฐ์‚ฐ ์ ์šฉ ์ˆœ์„œ๊ฐ€ ์ ์šฉ๋˜์ง€ ์•Š๋Š”๋‹ค.


There are three types of operations.
It is executed in the order of 'MetadataManipulator' -> 'OrderizedArbitraryManipulator' -> 'PostArbitraryManipulator'.

'MetadataManipulator' is executed in order of priority.
OrderedArbitraryManipulators and PostArbitraryManipulators are executed in the order in which operations are declared.

With specAny, the order of operation application set for the three types of operations is not applied.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.