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".