0

I can really use some help with this parameterized test case I am trying to create. No matter what kind of constructor I create the IDE gives an error message. Here is my code:

@RunWith(Parameterized.class)
public class SolverTest {
    final static File folder = new File("C:\\Users\\Azizam\\IdeaProjects\\EightPuzzle\\src\\ModifiedTests");
    final static String destFolder = "C:\\Users\\Azizam\\IdeaProjects\\EightPuzzle\\src\\testresults";
    final static ArrayList<Object[][]> filesList = new ArrayList<>();
    final Object currentBoard = new Object();

    @Parameterized.Parameters
    public static Iterable<Object[][]> data() {
        String path = "";
        int counter = 0;
        for (final File fileEntry : folder.listFiles()) {
            //System.out.println("processing file: " + fileEntry.getName())
            counter++;
            if (counter == 20) break;
            path = destFolder + fileEntry;
            In in = new In(fileEntry.getAbsolutePath());
            int n = in.readInt();
            int moves = in.readInt();
            int[][] tiles = new int[n][n];
            for (int i = 0; i < n; i++)
                for (int j = 0; j < n; j++)
                    tiles[i][j] = in.readInt();
            Board b = new Board(tiles);
            Object[][] fileList = new Object[][]{{b, moves}};
            filesList.add(fileList);
        }
        return filesList;
    }

    @Parameterized.Parameter(0)
    private Board board;
    @Parameterized.Parameter(1)
    private int expectedNumberOfMoves;

    public SolverTest(Board board, int expectedNumberOfMoves) {
        this.board = board;
        this.expectedNumberOfMoves = expectedNumberOfMoves;
    }


    @Test
    public void test() {
        assertEquals(expectedNumberOfMoves, new Solver(board).moves());
    }

}

I have tried different ways of creating a 1 parameter, 2, and no parameter constructors. But I have never seen this type of issue or what the solution might be. I am following this link and this tutorial. This is my first parameterized test, and debug does not seem to provide much for me either. I also saw these links, but they did not help. I can provide the code for the rest of the project also on GitHub or gist. I did debug my code through creating the fileList properly, but I know little about what happens to it afterwards or what needs to happen. Here is an excerpt of the error:

java.lang.Exception: Test class should have exactly one public zero-argument constructor

    at org.junit.runners.BlockJUnit4ClassRunner.validateZeroArgConstructor(BlockJUnit4ClassRunner.java:171)
    at org.junit.runners.parameterized.BlockJUnit4ClassRunnerWithParameters.validateConstructor(BlockJUnit4ClassRunnerWithParameters.java:90)
    at org.junit.runners.BlockJUnit4ClassRunner.collectInitializationErrors(BlockJUnit4ClassRunner.java:127)
    at org.junit.runners.ParentRunner.validate(ParentRunner.java:416)
    at org.junit.runners.ParentRunner.<init>(ParentRunner.java:84)
    at org.junit.runners.BlockJUnit4ClassRunner.<init>(BlockJUnit4ClassRunner.java:65)
    at org.junit.runners.parameterized.BlockJUnit4ClassRunnerWithParameters.<init>(BlockJUnit4ClassRunnerWithParameters.java:27)
    at org.junit.runners.parameterized.BlockJUnit4ClassRunnerWithParametersFactory.createRunnerForTestWithParameters(BlockJUnit4ClassRunnerWithParametersFactory.java:16)
    at org.junit.runners.Parameterized.createRunnersForParameters(Parameterized.java:313)
    at org.junit.runners.Parameterized.<init>(Parameterized.java:248)
    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
    at org.junit.internal.builders.AnnotatedBuilder.buildRunner(AnnotatedBuilder.java:104)
    at org.junit.vintage.engine.discovery.DefensiveAllDefaultPossibilitiesBuilder$DefensiveAnnotatedBuilder.buildRunner(DefensiveAllDefaultPossibilitiesBuilder.java:113)

Here is the latest version of my code:

package assignments;

import edu.princeton.cs.algs4.In;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

import java.io.File;
import java.util.ArrayList;

import static junit.framework.TestCase.assertEquals;

@RunWith(Parameterized.class)
public class SolverTest {
    final static File folder = new File("C:\\Users\\Azizam\\IdeaProjects\\EightPuzzle\\src\\ModifiedTests");
    final static String destFolder = "C:\\Users\\Azizam\\IdeaProjects\\EightPuzzle\\src\\testresults";
    final static ArrayList<Object[][]> filesList = new ArrayList<>();
    final Object currentBoard = new Object();

    @Parameterized.Parameters
    public static Iterable<Object[][]> data() {
        String path = "";
        int counter = 0;
        for (final File fileEntry : folder.listFiles()) {
            //System.out.println("processing file: " + fileEntry.getName())
            counter++;
            if (counter == 20) break;
            path = destFolder + fileEntry;
            In in = new In(fileEntry.getAbsolutePath());
            int n = in.readInt();
            int moves = in.readInt();
            int[][] tiles = new int[n][n];
            for (int i = 0; i < n; i++)
                for (int j = 0; j < n; j++)
                    tiles[i][j] = in.readInt();
            Board b = new Board(tiles);
            Object[][] fileList = new Object[][]{{b, moves}};
            filesList.add(fileList);
        }
        return filesList;
    }

    @Parameterized.Parameter(0)
    public Board board;
    @Parameterized.Parameter(1)
    public int expectedNumberOfMoves;

    public SolverTest() {
    }


    @Test
    public void test() {
        assertEquals(expectedNumberOfMoves, new Solver(board).moves());
    }

}

Here is a pic of the debug session showing everything I want in the file list. Some how the board object does not transfer to my Solver constructor. enter image description here

Shahin
  • 53
  • 8

1 Answers1

1

Debug Capture Here is what the no-arg constructor error means.

The constructor in the test class is the following:

public SolverTest(Board board, int expectedNumberOfMoves) {
    this.board = board;
    this.expectedNumberOfMoves = expectedNumberOfMoves;
}

That takes 2 arguments, so is not a no-arg constructor. The following is a no-arg constructor:

public SolverTest() {
}

Removing the 2-arg constructor will work, so this does not need to be explicitly listed, because the java compiler will add the default no-arg constructor automatically.

HOWEVER the reason for the error is a mix of 2 approaches for the Parameterized test class.

EITHER use a no-arg constructor with the @Parameterized.Parameters fields (which need to be public and not private, by the way), OR remove those fields and use the construct with arguments that take the parameters.

Here is the code modified to use the first approach (that is, with the @Parameterized.Parameters fields):

@RunWith(Parameterized.class)
public class SolverTest {
    final static File folder = new File("C:\\Users\\Azizam\\IdeaProjects\\EightPuzzle\\src\\ModifiedTests");
    final static String destFolder = "C:\\Users\\Azizam\\IdeaProjects\\EightPuzzle\\src\\testresults";
    final static ArrayList<Object[][]> filesList = new ArrayList<>();
    final Object currentBoard = new Object();

    @Parameterized.Parameters
    public static Iterable<Object[][]> data() {
        String path = "";
        int counter = 0;
        for (final File fileEntry : folder.listFiles()) {
            //System.out.println("processing file: " + fileEntry.getName())
            counter++;
            if (counter == 20) break;
            path = destFolder + fileEntry;
            In in = new In(fileEntry.getAbsolutePath());
            int n = in.readInt();
            int moves = in.readInt();
            int[][] tiles = new int[n][n];
            for (int i = 0; i < n; i++)
                for (int j = 0; j < n; j++)
                    tiles[i][j] = in.readInt();
            Board b = new Board(tiles);
            Object[][] fileList = new Object[][]{{b, moves}};
            filesList.add(fileList);
        }
        return filesList;
    }

    @Parameterized.Parameter(0)
    public Board board;
    @Parameterized.Parameter(1)
    public int expectedNumberOfMoves;

    public SolverTest() {
    }


    @Test
    public void test() {
        assertEquals(expectedNumberOfMoves, new Solver(board).moves());
    }

}
Shahin
  • 53
  • 8
ash
  • 4,867
  • 1
  • 23
  • 33
  • ah ok. I did not know I need to use one or the other way. Did not see that in the docs or the tutorial. Let me give it a shot. – Shahin Jul 13 '20 at 18:23
  • @Saaman I understand; after reformatting and reading through the entire thing more carefully, I see that the exception isn't the core problem. My answer is updated to address that. – ash Jul 13 '20 at 18:25
  • Yeah, it may be unclear that there are 2 competing methods there. The "no-arg" constructor error is thrown when the field-based approach is used, because the Parameterized class isn't then expecting to pass arguments to the constuctor. – ash Jul 13 '20 at 18:27
  • BTW, for easier to read and manage tests, I use cucumber - you might want to look at it. – ash Jul 13 '20 at 18:28
  • @Ash-Just tried the code. It gives me an error of "no runable methods". It looks like I am building the fileList in a way that does not transfer into the unit test. The error occurs in my Solver class where I check to make sure Board is not null – Shahin Jul 13 '20 at 18:39
  • Hmm, "no runnable methods" means that it is not finding any test method to run. I see the `@Test` method in the code, so not sure what is going on. – ash Jul 13 '20 at 21:14
  • @ash-Thank you so much. For some reason I could not click the check mark earlier. – Shahin Jul 15 '20 at 09:49