tags:

views:

1453

answers:

8

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

+1  A: 

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?

Tom Hawtin - tackline
+1  A: 

Having a no-arg constructor allows test classes to be aggregated into suites:

 TestSuite suite = new TestSuite();
 suite.add(TestClass.class);
 ...
JesperE
+3  A: 

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*)?

waxwing
+3  A: 

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.

ZZ Coder
+1  A: 

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.

Vineet Reynolds
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.
Thomas Baun
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
Oops my bad, line 396 creates the TestSuite whereas 386 creates the JUnit4TestAdapter.
Vineet Reynolds
@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.
Vineet Reynolds
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.
Thomas Baun
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.
Vineet Reynolds
+1  A: 

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 [http://stackoverflow.com/questions/672466/junit-how-to-avoid-no-runnable-methods-in-test-utils-classes%5D%5Bhere%5D.

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

Thomas Baun
Try exclude **/*Helper.java.
Rob Spieldenner
A: 

Non-static inner classes have a hidden constructor that takes the outer class as argument. If your inner classes dont share state with the outer classes, just make them static.

Adrian
A: 

For older versions of junit, if you have the word "Test" in your inner class, this problem will occur.

We ran into this problem while implementing the subclass-for-test pattern, naming the subclass, e.g., FooForTest. By renaming to FooSubclass, the problem was solved.

See the above comment from @Vineet Reynolds for more detail about affected junit versions, and why this happens for ant but not eclipse.

Hope that helps!

benvolioT