Skip to content

Collections passed to set() are shared across samples instead of copied (1.2.x regression) #1348

Closed

Description

@surecloud-jleite

Describe the bug

When a collection is passed to set(...), 1.2.x assigns that exact instance to the generated object instead of copying it. As a result, every sample() from the same builder returns an object that shares one collection instance, so mutating one sampled object mutates all of them.

It also means the collection type passed in leaks through. For example, Arrays.asList(...) produces a fixed-size list, so remove on the sampled object's list throws UnsupportedOperationException.

In 1.1.21 each sample received its own ArrayList copy. The 1.2.0 release notes say .set() is unchanged, so this looks unintended.

Your environment

  • Fixture Monkey 1.2.3 (fixture-monkey-starter). Works in 1.1.21.
  • Java 25 (Temurin 25.0.4.1), JUnit 5.13.4, AssertJ 3.27.7

Steps to reproduce

import static org.assertj.core.api.Assertions.assertThat;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.junit.jupiter.api.Test;

import com.navercorp.fixturemonkey.ArbitraryBuilder;
import com.navercorp.fixturemonkey.FixtureMonkey;
import com.navercorp.fixturemonkey.api.introspector.FieldReflectionArbitraryIntrospector;

class SetListInstanceTest {
    public static class Holder {
        private List<String> items;
    }

    private final FixtureMonkey fixtureMonkey = FixtureMonkey.builder()
        .objectIntrospector(FieldReflectionArbitraryIntrospector.INSTANCE)
        .build();

    @Test
    void samplesDoNotShareTheSetList() {
        ArbitraryBuilder<Holder> builder = fixtureMonkey.giveMeBuilder(Holder.class)
            .set("items", new ArrayList<>(List.of("a", "b")));

        Holder first = builder.sample();
        Holder second = builder.sample();
        first.items.add("c");

        assertThat(second.items).containsExactly("a", "b");
    }

    @Test
    void setFixedSizeListIsMutableOnSample() {
        Holder holder = fixtureMonkey.giveMeBuilder(Holder.class)
            .set("items", Arrays.asList("a", "b"))
            .sample();

        holder.items.remove(0);

        assertThat(holder.items).containsExactly("b");
    }
}

Both tests pass on 1.1.21 and fail on 1.2.3.

Expected behaviour

Each sample() gets its own collection instance, as in 1.1.x, so samples are independent of each other and of the value passed to set.

Actual behaviour

first.items == second.items is true, and second.items contains [a, b, c].

In the second test, the sampled list is the java.util.Arrays$ArrayList that was passed in, and remove(0) throws java.lang.UnsupportedOperationException.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions