Evgeni Dimitrov
Evgeni Dimitrov

Reputation: 22506

Spring Boot Test MalformedURLException: unknown protocol: classpath

If java.net.URL is used in a Spring Boot application, with classpath protocol, it works as expected, because Spring Boot registers URLStreamHandlerFactory. e.g. new URL("classpath: someFile.whatever").

But when this code is executed as JUnit test java.net.MalformedURLException: unknown protocol: classpathexception is thrown.

It seems that the appropriate URLStreamHandlerFactory is not registered when the Spring context is initialized for a JUnit test.

Steps to reproduce:

1) Create Spring Boot Starter project (e.g. using only starter Web).

2) add test.json file in src/main/resources

3) Add the following bean:

@Component
public class MyComponent {
    public MyComponent() throws MalformedURLException {
        URL testJson = new URL("classpath: test.json");
        System.out.println(testJson);
    }
}

4) Starting the app as java application works OK

5) Run the default "contextLoads" test:

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringUrlTestApplicationTests {

    @Test
    public void contextLoads() {
    }

}

java.net.MalformedURLException: unknown protocol: classpathexception is thrown.

What is the appropriate way to use URL with classpath resources in JUnit test?

In the real use-case I cannot alter the new URL("classpath: test.json") because it comes from 3th party library.

Tried to copy test.json in src/test/resources, to test if the error could be caused by a missing resource - no success.

Upvotes: 13

Views: 9269

Answers (5)

ahvargas
ahvargas

Reputation: 902

The problem is that the class that is handling the protocol is org.apache.catalina.webresources.TomcatURLStreamHandlerFactory. You can fix your test with this :

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class MalformedUrlApplicationTests {

    @Test
    public void contextLoads() {
    }

}

But i think @borino answer is better.

Upvotes: 1

Bradley M Handy
Bradley M Handy

Reputation: 613

Spring does not have a URLStreamHandler that can process URLs for the "classpath:" protocol. Spring handles this protocol with the ResourceLoader API. Once you load a resource, you can get the URL for it.

However, Java provides a mechanism for supporting non-standard protocols. I found a StackOverflow entry which should answer your question.

Supporting Classpath URLs

Upvotes: 0

borino
borino

Reputation: 1750

the shortest and easiest way is to create simple method before test execution, which is creating and registering handlers for 'classpath' protocol.

    @BeforeClass
    public static void init() {
        org.apache.catalina.webresources.TomcatURLStreamHandlerFactory.getInstance();
    }

I just checked and it works fine. This approach also using inside of spring-boot applications

Upvotes: 19

karans123
karans123

Reputation: 836

I prefer the following way using apache commons:

String url = IOUtils.toString(
    this.getClass().getResourceAsStream("test.json"),"UTF-8"
);
URL testJson = new URL(url);

File test.json => u can keep in src/test/resources folder -> it will be loaded from here, with maven it works for me

More methods like these are available here How to read a text-file resource into Java unit test?

Upvotes: 0

Hany Sakr
Hany Sakr

Reputation: 2939

I think you can try to Autowire the ResourceLoader, and load the file using resourceLoader.getResource("classpath:/test.json"), it will load the file from the resource folder.

Upvotes: 0

Related Questions