Search code examples
javaservletsintellij-ideagradlebuild.gradle

gradle test fail when using compileOnly and testCompileOnly


I have a small gradle library project which only have two files and two tests :

RandomUtils.java

final class RandomUtils {

    private static final SecureRandom random = new SecureRandom();

    private RandomUtils() {
    }

    static String nextRandomId() {
        return new BigInteger(130, random).toString(32);
    }

}

URLUtils.java:

public class URLUtils {

    private URLUtils() {
    }

    /**
     * Return the base url (eg: http://localhost:8080)
     * @param request
     * @return String
     * @throws MalformedURLException
     */
    public static String getURLBase(HttpServletRequest request) throws MalformedURLException {
        URL requestURL = new URL(request.getRequestURL().toString());
        String port = requestURL.getPort() == -1 ? "" : ":" + requestURL.getPort();
        return requestURL.getProtocol() + "://" + requestURL.getHost() + port;
    }

}

And these are the resepective tests :

@RunWith(JUnit4.class)
public class RandomUtilsTest {

    @Test
    public void testConstructorIsPrivate() throws Exception {
        Constructor constructor = RandomUtils.class.getDeclaredConstructor();
        assertTrue(Modifier.isPrivate(constructor.getModifiers()));
        constructor.setAccessible(true);
        constructor.newInstance();
    }

    @Test
    public void nextRandomId() throws MalformedURLException {
        assertNotNull(RandomUtils.nextRandomId());
    }

}

and

@RunWith(JUnit4.class)
public class URLUtilsTest {

    @Test
    public void testConstructorIsPrivate() throws Exception {
        Constructor constructor = URLUtils.class.getDeclaredConstructor();
        assertTrue(Modifier.isPrivate(constructor.getModifiers()));
        constructor.setAccessible(true);
        constructor.newInstance();
    }
    @Test
    public void getURLBase() throws MalformedURLException {
        MockHttpServletRequest request = new MockHttpServletRequest();
        request.setRequestURI("/entity");
        assertEquals("http://localhost", URLUtils.getURLBase((HttpServletRequest) request));
        request.setRequestURI(":8080/entity");
        assertEquals("http://localhost:8080", URLUtils.getURLBase((HttpServletRequest) request));
    }

}

I have the following build.gradle:

dependencies {
    compileOnly("javax.servlet:javax.servlet-api:${javaxServletApiVersion}")
    testCompileOnly("javax.servlet:javax.servlet-api:${javaxServletApiVersion}")
    testCompileOnly("org.springframework:spring-test:${springVersion}")
    testCompileOnly("junit:junit:${junitVersion}")
}

I don't have any problem in intellij with the dependencies but when I try to run ./gradlew clean build I have this error when running the tasks :common:webutils:test :

java.lang.NoClassDefFoundError: org/springframework/mock/web/MockHttpServletRequest
    at com.domain.api.common.webutils.URLUtilsTest.getURLBase(URLUtilsTest.java:27)

I have looked at the gradle documentation and it says that

  • compileOnly: Compile time only dependencies, not used at runtime
  • testCompileOnly: Additional dependencies only for compiling tests, not used at runtime

Is my configuration correct ? Why does it fail during the test if javax.servlet:javax.servlet-api:${javaxServletApiVersion} is present for both test and src ?


Solution

  • You answered your own question already:

    testCompileOnly: Additional dependencies only for compiling tests, not used at runtime

    testCompileOnly is just available when compiling the tests, not when executing them. If you need something only at test execution but not for compiling like slf4j bindings or similar, that belongs to testRuntimeOnly. If you need something for compiling the tests and also at runtime, then it belongs to testImplementation.