Does JUnit4 testclasses require a public no arg constructor?

22,441

Solution 1

Thank you all for your time and your answers. I have now found a solution. Previously I thought the input for the batchtest part of my ant target should be .class files but it is also possible to use .java files.

That solved the problem. Now it no longer complains about inner classes missing public constructors.

<target name="junit" description="Execute unit tests">
 <delete dir="tmp/rawtestoutput"/>
 <delete dir="test-reports"/>
    <mkdir dir="tmp/rawtestoutput"/>     
    <junit printsummary="on" failureproperty="junit.failure" fork="true">
      <jvmarg value="-Duser=TBA -Dpassword=ibber11"/>
        <classpath refid="class.path.test"/>
        <classpath refid="class.path.model"/>
        <classpath refid="class.path.gui"/>
        <classpath refid="class.path.jfreereport"/>
     <classpath path="tmp/${test.jar}"/>
      <batchtest todir="tmp/rawtestoutput">
        <fileset dir="src/test">
            <include name="**/*.java"/>
         <exclude name="**/SessionHelper.java"/>
         <exclude name="**/TestHelper.java"/>
        </fileset>
      </batchtest>
     <sysproperty key="user" value="tba"/>
     <sysproperty key="password" value="ibber11"/>
    </junit>
    <junitreport todir="tmp">
      <fileset dir="tmp/rawtestoutput"/>
      <report todir="test-reports"/>
    </junitreport>
    <fail if="junit.failure" message="Unit test(s) failed.  See reports!"/>
</target>

My only problem now is, that I have to filter out the test classes without tests, in order to avoid a "No runnable methods"-error. That is, the helper and util classes.

There must be a more elegant solution than mine. One solution could be a naming convention as suggested [JUnit: how to avoid "no runnable methods" in test utils classes.

The helper classes does not contain the @Test annotaion. It must be possible to utilize this in some way...

Solution 2

I believe you need a no-args constructor, but if you don't declare any constructors Java will create a synthetic one for you. Are you sure the ant-task is not picking up another class; one that just happens to follow the naming convention you've set (*Test or Test*)?

Solution 3

Eclipse uses a different implementation to execute JUnit4 test cases - it has its own test runner. This happens to be different from the one used by the Ant - the default one available in the JUnit distribution, and is the reason for the discrepancy noted in the execution behavior of the environments in Ant and Eclipse.

Taking a look at the source code of JUnit 4.3.1, 4.5 and 4.7 (especially that of the testrunners) reveals that test classes must have a public zero-arg constructor. Do note that the default runner in JUnit v4.7 is BlockJUnit4ClassRunner. You'll notice that the javadocs (what a pity!) contain the rules to be followed on what constitutes a well formed test class - a public zero-argument constructor is one of them.

Solution 4

You must have a default constructor for the test case. Otherwise, the runner doesn't know how to instantiate the class. Looks like you have a constructor with args. Java doesn't create default constructor if you already have a constructor.

Generally, you should avoid constructors in test cases. If you want do initialization, write a init method and annotate it with @BeforeClass. The benefit is that the stack trace will be much cleaner if you have any errors. As you can see, the constructor's stack trace is really confusing for most people.

Solution 5

Instances of your test classes need to be made somehow. You can create a no-arg test which adds test instances created in some other way, which can be useful for parameterising tests (or was in JUnit 3, anyway).

But why would you suppress the synthetic no-arg constructor?

Share:
22,441
Thomas Baun
Author by

Thomas Baun

Updated on January 17, 2020

Comments

  • Thomas Baun
    Thomas Baun over 4 years

    I have a test class, written in JUnit4 syntax, that can be run in eclipse with the "run as junit test" option without failing. When I run the same test via an ant target I get this error:

    java.lang.Exception: Test class should have public zero-argument constructor
    at org.junit.internal.runners.MethodValidator.validateNoArgConstructor(MethodValidator.java:54)
    at org.junit.internal.runners.MethodValidator.validateAllMethods(MethodValidator.java:39)
    at org.junit.internal.runners.TestClassRunner.validate(TestClassRunner.java:33)
    at org.junit.internal.runners.TestClassRunner.<init>(TestClassRunner.java:27)
    at org.junit.internal.runners.TestClassRunner.<init>(TestClassRunner.java:20)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
    at org.junit.internal.requests.ClassRequest.getRunner(ClassRequest.java:26)
    at junit.framework.JUnit4TestAdapter.<init>(JUnit4TestAdapter.java:24)
    at junit.framework.JUnit4TestAdapter.<init>(JUnit4TestAdapter.java:17)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
    at org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.run(JUnitTestRunner.java:386)
    at org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.launch(JUnitTestRunner.java:911)
    at org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner.main(JUnitTestRunner.java:768)
    Caused by: java.lang.NoSuchMethodException: dk.gensam.gaia.business.bonusregulering.TestBonusregulerAftale$Test1Reader.<init>()
    at java.lang.Class.getConstructor0(Class.java:2706)
    at java.lang.Class.getConstructor(Class.java:1657)
    at org.junit.internal.runners.MethodValidator.validateNoArgConstructor(MethodValidator.java:52)
    

    I have no public no arg constructor in the class, but is this really necessary?

    This is my ant target

    <target name="junit" description="Execute unit tests" depends="compile, jar-test">
            <delete dir="tmp/rawtestoutput"/>
            <delete dir="test-reports"/>
            <mkdir dir="tmp/rawtestoutput"/>
            <junit printsummary="true" failureproperty="junit.failure" fork="true">
              <classpath refid="class.path.test"/>
              <classpath refid="class.path.model"/>
              <classpath refid="class.path.gui"/>
              <classpath refid="class.path.jfreereport"/>
                <classpath path="tmp/${test.jar}"></classpath>
              <batchtest todir="tmp/rawtestoutput">
                <fileset dir="${build}/test">
                    <include name="**/*Test.class" />
                    <include name="**/Test*.class" />
                </fileset>
              </batchtest>
            </junit>
            <junitreport todir="tmp">
              <fileset dir="tmp/rawtestoutput"/>
              <report todir="test-reports"/>
            </junitreport>
            <fail if="junit.
    failure" message="Unit test(s) failed.  See reports!"/>
        </target>
    

    The test class have no constructors, but it has an inner class with default modifier. It also have an anonymouse inner class. Both inner classes gives the "Test class should have public zero-argument constructor error". I am using Ant version 1.7.1 and JUnit 4.7

  • Thomas Baun
    Thomas Baun over 14 years
    I was thinking the same as you. That it had something to do with junit3 vs junit4, but as far as I can see in the stacktrace it is actually a JUnit4TestAdapter that is involved.
  • Vineet Reynolds
    Vineet Reynolds over 14 years
    Well, I did take a look at the sources of Ant 1.7.1. In JUnitTestRunner.java (line 386 which appears in the stack trace) a TestSuite instance is created, only if the preconditions are met.
  • Vineet Reynolds
    Vineet Reynolds over 14 years
    Oops my bad, line 396 creates the TestSuite whereas 386 creates the JUnit4TestAdapter.
  • Vineet Reynolds
    Vineet Reynolds over 14 years
    @Thomas, which version of JUnit and Ant are you using? The reason is got to do with the presence of the TestRunner class itself in the stack trace.
  • Thomas Baun
    Thomas Baun over 14 years
    I am using TeamCity and the version bundled with it is 1.7.1. The Junit version is 4.7. This is the version I have specified in the classpath under my target. I have tried to run the same ant target in eclipse and the same version as above. Same errors.
  • Vineet Reynolds
    Vineet Reynolds over 14 years
    I believe I have the reason for this change in behavior. Eclipse has its own testrunner that is not as stringent as the one in JUnit.