takotsubo
takotsubo

Reputation: 746

JUnit parameterized test failed

I've just started with JUnit (read some articles before) and met problem. I've a parameterized test class:

@RunWith(value = Parameterized.class)
public class ConnectionTest {

    private Connection connection;

    @Parameterized.Parameter
    public String url;

    @Parameterized.Parameters()
    public static Collection<Object[]> urls() {
        return List.of(new Object[][]{
                {"https://www.google.com/"},
                {"..."},
                {"..."},
        });
    }

    @Parameterized.Parameter
    public String invalidUrl;

    @Parameterized.Parameter
    public String language;

    @Parameterized.Parameter
    public String country;

    public static Collection<Object[]> invalidUrls() {
        return List.of(new Object[][]{
                {"https://www.google.com/, "en", "US"},
                {...},
                {...}    
        });
    }

    @Before
    public void createConnection() {
        connection = new Connection();
    }

    @Test
    public void urlShouldBeLocalizedWithRequiredLanguage() throws MalformedURLException, URISyntaxException {
        Map<String, String> params = connection.getParameters(url);
        Assert.assertTrue(connection.getLocalized(params, "ru", "RU").toString().endsWith("&hl=en&gl=US"));
    }

    @Test
    public void parameterIdShouldNotBeEmpty() throws MalformedURLException {
        Assert.assertFalse(connection.getParameters(url).get("id").isEmpty());
    }

    @Test(expected = InvalidGooglePlayGameUrlException.class)
    public void notValidUrlShouldThrowException() throws InvalidUrlException, URISyntaxException, MalformedURLException {
        connection.connect(invalidUrl, language, country);
    }

}

I'll get following Exception:

@Parameter(0) is used more than once (4).
java.lang.Exception: @Parameter(0) is used more than once (4).
    at org.junit.runners.parameterized.BlockJUnit4ClassRunnerWithParameters.validateFields(BlockJUnit4ClassRunnerWithParameters.java:119)
    ...
....

And

@Parameter(1) is never used.
java.lang.Exception: @Parameter(1) is never used.

And some for Parameter(2,3).

Summary: 4 tests, all failed. Not even proper number of tests.

Or should I just left Parameterized tests and go with:

public class ConnectionTest {

    private Connection connection;

    private final List<String> urls = List.of(
            "some link",
            "some link"
    );

    private final List<String> invalidUrls = List.of(
            "some link",
            "some link"
    );

    @Before
    public void createConnection() {
        connection = new Connection();
    }

    @Test(expected = InvalidUrlException.class)
    public void invalidUrlShouldThrowException() throws InvalidUrlException, URISyntaxException, MalformedURLException {
        for (String url : invalidUrls) {
            connection.connect(url, "ru", "RU");
        }
    }

    @Test
    public void urlShouldBeLocalizedWithRequiredLanguage() throws MalformedURLException, URISyntaxException {
        for (String url : urls) {
            Map<String, String> params = connection.getParameters(url);
            Assert.assertTrue(connection.getLocalized(params, "ru", "RU").toString().endsWith("&hl=ru&gl=RU"));
            Assert.assertTrue(connection.getLocalized(params, "en", "US").toString().endsWith("&hl=en&gl=US"));
        }
    }

    @Test
    public void parameterIdShouldNotBeEmpty() throws MalformedURLException {
        for (String url : urls) {
            Assert.assertFalse(connection.getParameters(url).get("id").isEmpty());
        }
    }
}

Last variant works fine, but what about parameterized case? I don't really want to use for loops in each method.

Upvotes: 1

Views: 1386

Answers (2)

Stefan Birkner
Stefan Birkner

Reputation: 24520

You could structure your test with the Enclosed runner.

@RunWith(Enclosed.class)
public class TestClass {

    @RunWith(Parameterized.class)
    public static class ValidUrls {

        @Parameterized.Parameters()
        public static Collection<Object> urls() {
            return List.of(
                "https://www.google.com/",
                "...",
                "..."
             );
        }

        @Parameterized.Parameter
        public String url;

        private Connection connection;

        @Before
        public void createConnection() {
            connection = new Connection();
        }

        @Test
        public void urlShouldBeLocalizedWithRequiredLanguage() throws Exception {
            Map<String, String> params = connection.getParameters(url);
            Assert.assertTrue(connection.getLocalized(params, "ru", "RU").toString().endsWith("&hl=en&gl=US"));
        }

        @Test
        public void parameterIdShouldNotBeEmpty() throws Exception {
            Assert.assertFalse(connection.getParameters(url).get("id").isEmpty());
        }
    }

    public static class InvalidUrls {

        @Parameterized.Parameters()
        public static Object[][] invalidUrls() {
            return new Object[][]{
                new Object[] {"https://www.google.com/, "en", "US"},
                new Object[] {...},
                new Object[] {...}    
            });
        }

        @Parameterized.Parameter(0)
        public String invalidUrl;

        @Parameterized.Parameter(1)
        public String language;

        @Parameterized.Parameter(2)
        public String country;

        private Connection connection;

        @Before
        public void createConnection() {
            connection = new Connection();
        }

        @Test(expected = InvalidGooglePlayGameUrlException.class)
        public void notValidUrlShouldThrowException() throws Exception {
            connection.connect(invalidUrl, language, country);
        }
    }
}

For more options have a look at "Excluding a non param test in parameterized test class"

Upvotes: 0

Vladislav Varslavans
Vladislav Varslavans

Reputation: 2934

You need to split this test into two parameterized tests. As far as I know there can be only one list of data - only one @Parameterized.Parameters

One for nornal urls and one for invalid.

In invalid test you need to mark invalidUrls() as @Parameterized.Parameters

Additionally you must add indexes to @Parameterized.Parameter

For example here {"https://www.google.com/, "en", "US"}, you have 3 parameters.

Thus you must use

@Parameterized.Parameter(0)   // zero is default and not required 
public String invalidUrl;

@Parameterized.Parameter(1)
public String language;

@Parameterized.Parameter(2)
public String country;

Upvotes: 1

Related Questions