Darwin's Theories Blog

New Theories for a New Time

Testing Servlet Logic (MissingResourceException: Can't find bundle for base name javax.servlet.LocalStrings)

2020-05-20

Suppose youare unit testing code and you need to test some logic in an HttpServlet. There are several approaches:

  • Use mockito to mock the HttpServletRequest and HttpServletResponse;

  • Refactor so the logi does not depend on the Servlet API at all (best approach)

  • Other?

I had a servlet whose only goal in life was to redirect from some old URLs to new, including changing http parameters to part of the URL, so it couldn’t trivially be handled in the httpd config. I did refactor it so that the map() method took only a URL and the parameters, and returned the URL to redirect to. This new method has no dependencies on the Servlet API, so it should be testable as a unit test in isolation. So far, so good. It compiles: even better.

Running? Not so good.

[INFO] Running theories.MappingTest
[ERROR] Tests run: 2, Failures: 0, Errors: 2, Skipped: 0, Time elapsed: 1.322 s <<< FAILURE! - in theories.MappingTest
[ERROR] theories.MappingTest.goodMappingsShouldRedirect(String, String)[1]  Time elapsed: 0.37 s  <<< ERROR!
java.lang.ExceptionInInitializerError
	at theories.MappingTest.goodMappingsShouldRedirect(MappingTest.java:23)
Caused by: java.util.MissingResourceException: Can't find bundle for base name javax.servlet.LocalStrings, locale en_US
	at theories.MappingTest.goodMappingsShouldRedirect(MappingTest.java:23)
[ERROR] theories.MappingTest.goodMappingsShouldRedirect(String, String)[2]  Time elapsed: 0.009 s  <<< ERROR!
java.lang.NoClassDefFoundError: Could not initialize class theories.MappingServlet
	at theories.MappingTest.goodMappingsShouldRedirect(MappingTest.java:23)

Seems that the Servlet code has a static initializer that loads some strings for Internationalization.

You ight think you could just add the Servlet API to the test scope, but it’s already there in provided scope, and you can’t pass more than one scope to a dependency.

One solution would be to move the map() method into its own class. But being lazy, I found the harder way forward :-)

I made an educated guess that, if you don’t call the Servlet’s init() or doGet() methods, the strings in the messages file won’t be looked up.

Under Maven (or gradle), there is a special place for resource files that are only used in testing. Thus, creating an empty file named LocalString.properties was sufficient, as long as it’s placed in src/test/resource/javax/servlet.

mkdir -p src/test/resources/javax/servlet
touch $_/LocalStrings.properties

Then a quick mvn test reveals that I’m half-way there, for there is a second properties file to fake out.

mkdir -p src/test/resources/javax/servlet/http
touch $_/LocalStrings.properties

Et voila! Now I can test the Servlet logic without having to mock some hairy objects and without having to do any other fancy song and dance. It "just works".