0

Description

I am testing a bulk insert in a mobgoDB database, in the @BeforeEach method I instantiate the database, in the@AfterEach I delete all documents created in the puzzles-today collection.

But the last one always fails, have random result, sometimes the test passes sometimes it displays this 367 or 366 or 364 or 364.

I added @RepeatedTest(4) To make sure it doesn't vary.

The code :

Class test

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.util.List;

import com.github.mathieusoysal.codingame_stats.CodinGame;
import com.github.mathieusoysal.codingame_stats.puzzle.Puzzle;

import org.bson.Document;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.RepeatedTest;
import org.junit.jupiter.api.Test;

import io.github.mathieusoysal.util.MongoDBMockTest;

public class PuzzleDaoTest extends MongoDBMockTest {

    private PuzzlesDao puzzleDao;

    @BeforeEach
    public void setUp() throws Exception {
        super.setUp();
        puzzleDao = new PuzzlesDao(mongoClient, "CodinGame-stats");
    }

    @AfterEach
    public void tearDown() throws Exception {
        mongoClient.getDatabase("CodinGame-stats")
                .getCollection(PuzzlesDao.PUZZLES_HISTORY_COLLECTION)
                .deleteMany(new Document());
        super.tearDown();
    }

    @Test
    void testSaveAll_shouldReturnTrue_withTwoPuzzles() {
        List<Puzzle> puzzles = new CodinGame().getPuzzles().subList(0, 2);
        assertTrue(puzzleDao.saveAll(puzzles));
    }

    @Test
    void testSaveAll_shouldAugmentCollectionSize_withTwoPuzzles() {
        List<Puzzle> puzzles = new CodinGame().getPuzzles().subList(0, 2);
        puzzleDao.saveAll(puzzles);
        assertEquals(puzzles.size(), countDocuments());
    }

    @RepeatedTest(4)
    void testSaveAll_shouldAugmentCollectionSize_withAllPuzzles() {
        List<Puzzle> puzzles = new CodinGame().getPuzzles();
        puzzleDao.saveAll(puzzles);
        assertEquals(puzzles.size(), countDocuments());
    }

    private long countDocuments() {
        return mongoClient.getDatabase("CodinGame-stats")
                .getCollection(PuzzlesDao.PUZZLES_HISTORY_COLLECTION)
                .countDocuments();
    }

}

Clone the project

To easily replicate this, you can clone the repository in the dev branch https://github.com/MathieuSoysal/CodinGame-Puzzles-stats-saver/tree/dev

Error displayed :

enter image description here

But this result is random, sometimes the test passes sometimes it displays a error.

anyone have any idea to fix this?

10
  • 3
    There’s too much unknown stuff going on here. I suggest you create a minimum example with all details to replicate your problem. Otherwise it’s hard to figure it out for outside people. Commented Jan 4, 2022 at 20:54
  • 1
    @johanneslink Thank for your feed-back, I added more details to replicate the problem. Commented Jan 4, 2022 at 21:03
  • 2
    What’s the failure message? Commented Jan 5, 2022 at 6:58
  • 1
    I edited my question to add the failure message. Commented Jan 5, 2022 at 18:57
  • 1
    If you want to raise chances that somebody will help figure out your problem, make replicating the issue for them as easy as possible. Having to put my own pom.xml together and copying several sources into a project that I have to configure myself is way beyond my threshold. Other people‘s mileage may vary. Commented Jan 6, 2022 at 16:13

1 Answer 1

1

The problem you have is not a test problem, but an implementation problem. Look at:

class PuzzlesDao...
    public boolean saveAll(List<Puzzle> puzzles) {
        List<WriteModel<Document>> bulkWrites = new ArrayList<>();
        GsonBuilder builder = new GsonBuilder();
        Gson gson = builder.create();
        puzzles.parallelStream()
                .forEach(puzzle -> bulkWrites.add(new InsertOneModel<>(Document.parse(gson.toJson(puzzle)))));
        BulkWriteOptions bulkWriteOptions = new BulkWriteOptions().ordered(false);
        BulkWriteResult bulkWriteResult = collection.bulkWrite(bulkWrites, bulkWriteOptions);
        return bulkWriteResult.wasAcknowledged();
    }

You are using parallelStream() without a closing operation like collect(..) or count(). The forEach code is still being executed while the bulk operation is already running. Thus the resulting behaviour is unpredictable. One heuristic is to never use any side-effects in forEach(). If you have to then using a simple for loop is the better choice in my opinion.

I recommend the following change:

public boolean saveAll(List<Puzzle> puzzles) {
    GsonBuilder builder = new GsonBuilder();
    Gson gson = builder.create();
    List<WriteModel<Document>> bulkWrites =
            puzzles.parallelStream()
                    .map(puzzle -> new InsertOneModel<>(Document.parse(gson.toJson(puzzle))))
                    .collect(Collectors.toList());
    BulkWriteOptions bulkWriteOptions = new BulkWriteOptions().ordered(false);
    BulkWriteResult bulkWriteResult = collection.bulkWrite(bulkWrites, bulkWriteOptions);
    return bulkWriteResult.wasAcknowledged();
}

I personally hardly ever use parallelStream() unless I can show that it improves performance in a worthwhile way, which it usually does not. Using just stream() gets rid of the intricacies of parallelism.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you for your answer ! I just changed .toList() instead of .collect(Collectors.toList()).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.