I am puzzled... when I run a JUnit test, there is no main method in the class and it still runs. Alo, a java/maven web app does not have a main anywhere. My assumption is that it is hidden somewhere on some class I somehow extend or something. Can someone please explain is it there, and it yes, how to find out where it is?
Thank you in advance
That's a good question!
JUnit and Servlets (basically your web apps) are frameworks. What a framework does is it runs the code you write which adheres to some common guidelines (e.g. in the case of a JUnit test, you either extend the class with some JUnit class or decorate the methods with the
As to how it happens, its done entirely via reflection in both the cases. JUnit has the concept of "runners" or basically classes which run the test code. When you right click on the file and say run as JUnit test case, the Eclipse JUnit plugin calls one of the runner classes (in case of Eclipse
BlockJUnit4ClassRunner) with the fully qualified name of your class to be tested. After that, it's all reflection magic by creating an instance of your test class, grabbing the special JUnit methods and executing them in some sort of wrapper/controlled environment.
In case of web apps, you specify the servlet fully qualified name of the servlet class in the
web.xml file. The servlet container takes this name, tries to load and instantiate the class dynamically and if it succeeds, casts the object an appropriate servlet type (e.g.
HttpServlet). I know this is all hand-waving and the details might deviate but the general idea remains the same. The web container/servlet container is very much aware of all the applications you have deployed, the URL's it is supposed to handle, the mapping between a given URL and a servlet etc. Thus, when a container starts, it loads up all this data and inside the main method (which is responsible for starting up the container), sets up a procedure which can handle requests at a given port and other things (which are specified in the
servlet.xml file I believe).
Hope that explains stuff a bit.
EDIT: The bit about finding out which classes are executing your code, it's pretty simple. Just throw an exception in your code and the exception trace will show all the guilty classes involved in the entire process. This might not work always in case someone decides to mess around with strack traces but it works most of the time. For e.g. throwing an exception in my JUnit code gives me the following trace:
java.lang.RuntimeException: HI at sos.TestMe.test(TestMe.java:11) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:601) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20) at org.junit.runners.BlockJUnit4ClassRunner.runNotIgnored(BlockJUnit4ClassRunner.java:79) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:71) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184) at org.junit.runners.ParentRunner.run(ParentRunner.java:236) at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50) at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
From the above, it's pretty easy to see that it is the Eclipse plugin code which gets executed first, followed by the
BlockJUnit4ClassRunner (note that
ParentRunner is the super-classs of
thank you very much for your response. It is weird how I missed the idea of throwing an exception to see where main is. Thank you for that. This question might not be something extremely practical but now that I know what you explained, I am more confident with experimenting.