13

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.

Evgeni Dimitrov
  • 21,976
  • 33
  • 120
  • 145
  • If you can't change 3th party library [define the URLhandler](https://stackoverflow.com/questions/861500/url-to-load-resources-from-the-classpath-in-java), otherwise easy to using @Resource - [examples](https://www.baeldung.com/spring-annotations-resource-inject-autowire) – borino Oct 23 '18 at 08:31

5 Answers5

19

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

borino
  • 1,720
  • 1
  • 16
  • 24
1

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.

ahvargas
  • 903
  • 10
  • 17
0

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.

Hany Sakr
  • 2,591
  • 28
  • 27
  • Where should I do that? In the Test class? As I said I would like to avoid changing the library that contains the code with java.net.URL. – Evgeni Dimitrov Oct 25 '18 at 07:59
  • There are many options here, using a static file at your resource folder under test, or mocking the JSON as a string Another solution, If you need integration testing that will call the actual URL, just simply call the implementation method which internally will call the actual URL (as long as you are using @SpringBootTest), everything will be loaded and the URL will be called, then you could do any testing or mocking. – Hany Sakr Oct 25 '18 at 09:26
  • https://www.petrikainulainen.net/programming/spring-framework/unit-testing-of-spring-mvc-controllers-rest-api/ – Hany Sakr Oct 25 '18 at 09:31
0

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?

karans123
  • 796
  • 6
  • 8
0

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

Bradley M Handy
  • 603
  • 6
  • 15