Java Testing

junit

Man erzeugt eine neue Klasse, in der mit @Test markierte Methoden einzelne Tests ausführen

import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;

public class MyCTestClass
{
  @Before
  public void Myinit()
  {
   ...
  }

  @Test
  public void TestAddMethod()
  {
        assertTrue(a.add(ZERO).equals(a));
        assertTrue(a.add(b).equals(b.add(a)));
          }

  @Test
  public void isDivOK()
  {
        assertTrue(a.div(ONE).equals(a));
        assertTrue(a.mult(b).div(b).equals(a));
                  }

  @Test(expected=NullPointerException.class)
  public void nullParameter()
  {
        a.add(null);
  }

}

Diese Tests können dann über die Kommandozeile, die IDE oder im Buildprozess ausgeführt werden.

Man sollte sich vorher überlegen, ob man die JUnit Testklassen direkt im normalen Source Ordner ablegt, wo auch die zu testenden Klassen liegen

MyProject
 build
 src
  de
   tgunkel
    Foo.src
    FooTest.src

oder ob man parallel zum Source Ordner eine weitere Ordner Hierarchie einzieht in der dann die Tests liegen

MyProject
 build
 src
  de
   tgunkel
    Foo.src
 src_tests
  de
   tgunkel
    FooTest.src

Legt man sie im selben Ordner ab, werden die Klassen direkt mitkompiliert und man muss seinen Buildprozess nicht anpassen. Dafür ist es aufwendiger die Tests vom normalen Code zu trennen, z.B. wenn man die Testklassen nicht mit ausliefern möchte. Das Ergebnis kann man dann in einem Ordner ablegen lassen. Andere Tools können die Ergebnisse in diesem Ordner dann auswerten.

If you wan to compare two container use hamcrest

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.contains;

List<String> result=...
assertThat(result, contains("foo", "bar"));

If order should not matter use

import static org.hamcrest.Matchers.hasItems;

instead.

If you have 2 lists you can also do it like this

import static org.hamcrest.CoreMatchers.is;

assertThat(actual, is(expected));

If tests should not run under all conditions (e.g. not on all days)

import static org.junit.Assume.*

 @Test public void test() {
   assumeThat(true);
   assertEquals(..., ...);
}

JUnit Test über die Kommandozeile

java org.junit.runner.JUnitCore MyCTestClass

JUnit Tests in Eclipse

Eclipse unterstützt direkt JUnit Tests

Eclipse JUnit

JUnit Tests mit ant

Man kann direkt aus ant heraus die JUnit Tests durchlaufen lassen

<property name="junitTestResults"     location="_junit_test_results/" />

<path id="classpath">
  <fileset file="MyProject.jar" />
</path>

<path id="classpath_tests">
  <fileset file="MyProjectTests.jar" />
  <fileset file="junit-4.8.1.jar" />
</path>

<target name="unittests" description="Execute unit tests" depends="compile_tests,jar_tests">
  <echo message="Unit tests ..." />
  <junit printsummary="withOutAndErr" fork="no" failureproperty="junit.failure" haltonfailure="false">

    <classpath refid="classpath" />
    <classpath refid="classpath_tests" />

    <formatter type="xml" usefile="true" />

    <batchtest todir="${junitTestResults}">
      <fileset dir="src_tests">
       <include name="**/*.java"/>
      </fileset>
    </batchtest>

  </junit>
  <echo message="Unit tests done" />
</target>

So kann man steuern, ob mögliche Stacktraces auf der Konsole landen <!-- <formatter type="xml" usefile="true" /> --> <formatter type="plain" usefile="false"/>

JUnit und log4j

public class myTestClass
{
 @BeforeClass
 public static void staticInit()
 {
        // make log4j happy
        BasicConfigurator.configure();
 }
}

Junit, ant und Stacktrace für Tests auf Console

Mockito

Wenn es für Unit Tests zu aufwendig ist Testobjekte zu erstellen kann man mit Mockito leere Objekte erstellen lassen die dann bei bestimmten Aufrufen die Werte zurückliefern, die man für Tests braucht. Anstatt z.B. für einen Test wirklich eine Collection mit sehr vielen Elementen zu erzeugen, kann man so eine Mock Collection erzeugen die einfach behauptet sie hätte die gewünschte Anzahl an Elementen.

@RunWith(MockitoJUnitRunner.class)
public class OfferListViewWithStatsTest {

    @Test
    public void test() {

        final Collection tst = Mockito.mock(Collection.class);
        Mockito.when(tst.size()).thenReturn(10000000);

        // just create an empty Mock object which claims to be of the right class
        static final List<String> list5= Mockito.mock(List.class);

        // if anybody asks the test object about its size, just lie and say its 5
        Mockito.when(list5.size()).thenReturn(5);

        // if anybody wants to get an element from the list, just return the same element. But also check that the position is within the size we already lied about
        Mockito.when(list5.get(ArgumentMatchers.anyInt())).then(new Answer<String>() {
            @Override
            public String answer(final InvocationOnMock invocation) throws Throwable {
                final Object[] args = invocation.getArguments();
                final int pos = (int) args[0];
                if (pos >= 0 && pos < 5) {
                   return "DUMMY";
                }
                throw new IndexOutOfBoundsException();
            }
        });

        // now we are ready to use the test object
        assertEquals(5, TestValues.list5.size());
        assertEquals("DUMMY", TestValues.list5.get(4));
    }
}

If you want to mock a method call that has parameters use org.mockito.ArgumentMatchers, e.g. ArgumentMatchers.any()

Hudson / Jenkins

Hudson (Oracle) bzw. Jenkins (freier Fork) ist ein Tool zur kontinuierlichen Überwachung von Softwareprojekten. Damit kann man z.B. periodisch aus einem Subversion Server Code auschecken, in compilieren und danach die Ergebisse der JUnit Test überprüfen. Über die Ergebnisse können die Entwickler dann direkt informiert werden.