JUnit 4: Wie man die eigene Liste testen kann [TDD]

B

BestGoalkeeper

Bekanntes Mitglied
@mihe7 hat auch schon so ein Thema gemacht, da dachte ich mache ich auch mal.

Zuerst unsere eigene Liste-Klasse:
Java:
import java.util.ArrayList;
import java.util.List;

public class MyIntList extends ArrayList<Integer> implements List<Integer> {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
}
Es ist also eine Integer-List. Da ich etwas faul war, hab ich von ArrayList<Integer> abgeleitet und die Methoden nicht selber implementiert - das müsstet ihr natürlich tun.

Dann unsere Test-Klasse, welche die wichtigen Methoden testen sollte:
Java:
import static org.junit.Assert.*;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Random;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

public class MyIntListTest {
	private final MyIntList L1 = new MyIntList();

	@BeforeClass
	public static void setUpBeforeClass() throws Exception {
	}

	@AfterClass
	public static void tearDownAfterClass() throws Exception {
	}

	@Before
	public void setUp() throws Exception {
	}

	@After
	public void tearDown() throws Exception {
	}

	public List<Integer> createRandomTestListWithAtLeast4Elements() {
		Random random = new Random();
		List<Integer> l2 = new ArrayList<Integer>();
		for (int i = 0; i < 4; i++) {
			l2.add(random.nextInt(10));
		}
		while (random.nextFloat() <= 0.8706f) {
			l2.add(random.nextInt(10));
		}
		return l2;
	}

	@Test
	public void testSize() {
		assertEquals(L1.size(), 0);
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		L1.addAll(l2);
		assertEquals(L1.size(), l2.size());
		L1.clear();
	}

	@Test
	public void testIsEmpty() {
		assertEquals(L1.isEmpty(), true);
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		L1.addAll(l2);
		assertEquals(L1.isEmpty(), false);
		L1.clear();
	}

	@Test
	public void testContains() {
		assertEquals(L1.contains(42), false);
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		L1.addAll(l2);
		assertEquals(L1.contains(l2.get(1)), true);
		L1.clear();
	}

	@Test
	public void testIterator() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		L1.addAll(l2);
		Iterator<Integer> iterator1 = L1.iterator();
		Iterator<Integer> iterator2 = l2.iterator();
		while (iterator1.hasNext()) {
			assertEquals(iterator2.hasNext(), true);
			assertEquals(iterator1.next(), iterator2.next());
		}
		L1.clear();
	}

	@Test
	public void testToArray() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		L1.addAll(l2);
		Object[] array1 = L1.toArray();
		Object[] array2 = l2.toArray();
		assertArrayEquals(array1, array2);
		L1.clear();
	}

	@Test
	public void testToArrayTArray() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		L1.addAll(l2);
		@SuppressWarnings("unused")
		Exception exception = assertThrows(ArrayStoreException.class, () -> L1.toArray(new String[0]));
		Integer[] array1 = L1.toArray(new Integer[0]);
		Integer[] array2 = l2.toArray(new Integer[0]);
		assertArrayEquals(array1, array2);
		L1.clear();
	}

	@Test
	public void testAddInteger() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		L1.add(l2.get(0));
		assertEquals(L1.get(0), l2.get(0));
		L1.clear();
	}

	@Test
	public void testRemoveObject() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		L1.addAll(l2);
		assertEquals(L1.remove((Object) l2.get(1)), true);
		L1.clear();
	}

	@Test
	public void testContainsAll() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		L1.addAll(l2);
		assertEquals(L1.containsAll(l2), true);
		assertEquals(l2.containsAll(L1), true);
		L1.clear();
	}

	@Test
	public void testAddAllCollectionInteger() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		L1.addAll((Collection<Integer>) l2);
		assertEquals(L1.containsAll(l2), true);
		assertEquals(l2.containsAll(L1), true);
		L1.clear();
	}

	@Test
	public void testRemoveAll() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		L1.addAll(l2);
		L1.removeAll(l2);
		assertEquals(L1.size(), 0);
	}

	@Test
	public void testRetainAll() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		L1.addAll(l2);
		L1.retainAll(l2);
		assertEquals(L1.size(), l2.size());
		L1.clear();
	}

	@Test
	public void testClear() {
		assertEquals(L1.size(), 0);
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		L1.addAll(l2);
		assertNotEquals(L1.size(), 0);
		L1.clear();
		assertEquals(L1.size(), 0);
	}

	@Test
	public void testGet() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		L1.addAll(l2);
		for (int i = 0; i < L1.size(); i++) {
			assertEquals(L1.get(i), l2.get(i));
		}
		L1.clear();
	}

	@Test
	public void testSet() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		L1.addAll(l2);
		assertEquals(L1.set(1, 42), l2.get(1));
		assertEquals(L1.get(1), Integer.valueOf(42));
		L1.clear();
	}

	@Test
	public void testAddIntInteger() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		L1.addAll(l2);
		L1.add(1, 42);
		assertEquals(L1.get(1), Integer.valueOf(42));
		L1.clear();
	}

	@Test
	public void testRemoveInt() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		L1.addAll(l2);
		assertEquals(L1.remove(1), l2.get(1));
		assertNotEquals(L1.get(1), Integer.valueOf(42));
		L1.clear();
	}

	@Test
	public void testIndexOf() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		L1.addAll(l2);
		assertEquals(L1.indexOf(L1.get(1)), l2.indexOf(l2.get(1)));
		assertEquals(L1.indexOf(42), -1);
		L1.clear();
	}

	@Test
	public void testLastIndexOf() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		L1.addAll(l2);
		assertEquals(L1.lastIndexOf(L1.get(1)), l2.lastIndexOf(l2.get(1)));
		assertEquals(L1.lastIndexOf(42), -1);
		L1.clear();
	}
}

Wenn alle Methoden bei euch richtig sind, sollten alle Tests grün werden. :)

Nachteile:
- addAll sollte früh implementiert werden, da diese Methode in vielen Tests verwendet wird.
 
sascha-sphw

sascha-sphw

Bekanntes Mitglied
@mihe7 hat auch schon so ein Thema gemacht, da dachte ich mache ich auch mal.

Zuerst unsere eigene Liste-Klasse:
Java:
import java.util.ArrayList;
import java.util.List;

public class MyIntList extends ArrayList<Integer> implements List<Integer> {
    /**
     *
     */
    private static final long serialVersionUID = 1L;
}
Es ist also eine Integer-List. Da ich etwas faul war, hab ich von ArrayList<Integer> abgeleitet und die Methoden nicht selber implementiert - das müsstet ihr natürlich tun.

Dann unsere Test-Klasse, welche die wichtigen Methoden testen sollte:
Java:
import static org.junit.Assert.*;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Random;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

public class MyIntListTest {
    private final MyIntList L1 = new MyIntList();

    @BeforeClass
    public static void setUpBeforeClass() throws Exception {
    }

    @AfterClass
    public static void tearDownAfterClass() throws Exception {
    }

    @Before
    public void setUp() throws Exception {
    }

    @After
    public void tearDown() throws Exception {
    }

    public List<Integer> createRandomTestListWithAtLeast4Elements() {
        Random random = new Random();
        List<Integer> l2 = new ArrayList<Integer>();
        for (int i = 0; i < 4; i++) {
            l2.add(random.nextInt(10));
        }
        while (random.nextFloat() <= 0.8706f) {
            l2.add(random.nextInt(10));
        }
        return l2;
    }

    @Test
    public void testSize() {
        assertEquals(L1.size(), 0);
        List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
        L1.addAll(l2);
        assertEquals(L1.size(), l2.size());
        L1.clear();
    }

    @Test
    public void testIsEmpty() {
        assertEquals(L1.isEmpty(), true);
        List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
        L1.addAll(l2);
        assertEquals(L1.isEmpty(), false);
        L1.clear();
    }

    @Test
    public void testContains() {
        assertEquals(L1.contains(42), false);
        List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
        L1.addAll(l2);
        assertEquals(L1.contains(l2.get(1)), true);
        L1.clear();
    }

    @Test
    public void testIterator() {
        List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
        L1.addAll(l2);
        Iterator<Integer> iterator1 = L1.iterator();
        Iterator<Integer> iterator2 = l2.iterator();
        while (iterator1.hasNext()) {
            assertEquals(iterator2.hasNext(), true);
            assertEquals(iterator1.next(), iterator2.next());
        }
        L1.clear();
    }

    @Test
    public void testToArray() {
        List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
        L1.addAll(l2);
        Object[] array1 = L1.toArray();
        Object[] array2 = l2.toArray();
        assertArrayEquals(array1, array2);
        L1.clear();
    }

    @Test
    public void testToArrayTArray() {
        List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
        L1.addAll(l2);
        @SuppressWarnings("unused")
        Exception exception = assertThrows(ArrayStoreException.class, () -> L1.toArray(new String[0]));
        Integer[] array1 = L1.toArray(new Integer[0]);
        Integer[] array2 = l2.toArray(new Integer[0]);
        assertArrayEquals(array1, array2);
        L1.clear();
    }

    @Test
    public void testAddInteger() {
        List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
        L1.add(l2.get(0));
        assertEquals(L1.get(0), l2.get(0));
        L1.clear();
    }

    @Test
    public void testRemoveObject() {
        List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
        L1.addAll(l2);
        assertEquals(L1.remove((Object) l2.get(1)), true);
        L1.clear();
    }

    @Test
    public void testContainsAll() {
        List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
        L1.addAll(l2);
        assertEquals(L1.containsAll(l2), true);
        assertEquals(l2.containsAll(L1), true);
        L1.clear();
    }

    @Test
    public void testAddAllCollectionInteger() {
        List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
        L1.addAll((Collection<Integer>) l2);
        assertEquals(L1.containsAll(l2), true);
        assertEquals(l2.containsAll(L1), true);
        L1.clear();
    }

    @Test
    public void testRemoveAll() {
        List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
        L1.addAll(l2);
        L1.removeAll(l2);
        assertEquals(L1.size(), 0);
    }

    @Test
    public void testRetainAll() {
        List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
        L1.addAll(l2);
        L1.retainAll(l2);
        assertEquals(L1.size(), l2.size());
        L1.clear();
    }

    @Test
    public void testClear() {
        assertEquals(L1.size(), 0);
        List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
        L1.addAll(l2);
        assertNotEquals(L1.size(), 0);
        L1.clear();
        assertEquals(L1.size(), 0);
    }

    @Test
    public void testGet() {
        List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
        L1.addAll(l2);
        for (int i = 0; i < L1.size(); i++) {
            assertEquals(L1.get(i), l2.get(i));
        }
        L1.clear();
    }

    @Test
    public void testSet() {
        List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
        L1.addAll(l2);
        assertEquals(L1.set(1, 42), l2.get(1));
        assertEquals(L1.get(1), Integer.valueOf(42));
        L1.clear();
    }

    @Test
    public void testAddIntInteger() {
        List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
        L1.addAll(l2);
        L1.add(1, 42);
        assertEquals(L1.get(1), Integer.valueOf(42));
        L1.clear();
    }

    @Test
    public void testRemoveInt() {
        List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
        L1.addAll(l2);
        assertEquals(L1.remove(1), l2.get(1));
        assertNotEquals(L1.get(1), Integer.valueOf(42));
        L1.clear();
    }

    @Test
    public void testIndexOf() {
        List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
        L1.addAll(l2);
        assertEquals(L1.indexOf(L1.get(1)), l2.indexOf(l2.get(1)));
        assertEquals(L1.indexOf(42), -1);
        L1.clear();
    }

    @Test
    public void testLastIndexOf() {
        List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
        L1.addAll(l2);
        assertEquals(L1.lastIndexOf(L1.get(1)), l2.lastIndexOf(l2.get(1)));
        assertEquals(L1.lastIndexOf(42), -1);
        L1.clear();
    }
}

Wenn alle Methoden bei euch richtig sind, sollten alle Tests grün werden. :)

Nachteile:
- addAll sollte früh implementiert werden, da diese Methode in vielen Tests verwendet wird.

Bei @mihe7 s Themen steht ja ein Bedarf dahinter. Bei Deinem kann ich das leider nicht erkennen? Warum sollte man sowas wollen?
Und was das mit TDD zu tun hat kann ich leider auch nicht erkennen.
 
B

BestGoalkeeper

Bekanntes Mitglied
Diese Fragen kommen immer wieder mal. Und TDD gibt dir vor, was du zu tun hast - ist nicht so schwer zu begreifen.
Ach so... kann man sich ein full quote nicht sparen?
 
sascha-sphw

sascha-sphw

Bekanntes Mitglied
Diese Fragen kommen immer wieder mal. Und TDD gibt dir vor, was du zu tun hast - ist nicht so schwer zu begreifen.
Ich sehe aber in Deinem Beispiel nichts das irgendwie erklärt was TDD ist und wie man da ran geht, zumal bei Dir auch die Implementierung als erstes kommt. Ich find es ja nicht schlecht, dass Du etwas vermitteln möchtest, aber dann sollte dazu auch was im Beitrag stehen.

Des weiteren würde ich ein anderes Beispiel dafür nehmen, etwas das ggf. näher an der Realität ist. Ein ArrayList<Integer> über Vererbung zu realisieren, da könnte ja der ein oder andere dann wirklich glauben das man das so macht.

Ach so... kann man sich ein full quote nicht sparen?
Hätte ich nicht müssen, aber Macht der Gewohnheit dann doch einfach nur auf Antworten geklickt.
 
B

BestGoalkeeper

Bekanntes Mitglied
und benutzt die korrekten Begriffe
verwende ich auch.

Also nochmal kurz als Erklärung für die, die TDD noch nicht kennen: Man schreibt zuerst die Tests und anschließend anhand der Tests die Implementierungen. Konkret heißt das, dass eine das Interface List<Integer> implementierende Klasse zu schreiben ist. Wie die Tests formuliert werden können habe ich oben angegeben.

Kannst du sagen, was deiner Ansicht nach falsch ist, oder die Klappe halten? Danke.
 
mrBrown

mrBrown

Super-Moderator
Mitarbeiter
Also nochmal kurz als Erklärung für die, die TDD noch nicht kennen: Man schreibt zuerst die Tests und anschließend anhand der Tests die Implementierungen. Konkret heißt das, dass eine das Interface List<Integer> implementierende Klasse zu schreiben ist.
Das ist eine so starke verkürzte Wiedergabe, dass sie kaum noch richtig ist, die relevanten Punkte hat du weggelassen. Du beschreibst „Tests First“, mit Test Driven Development hat das aber nicht wirklich was zu tun.


Kannst du sagen, was deiner Ansicht nach falsch ist, oder die Klappe halten? Danke.
Ich sagte schlecht, nicht falsch ;)

Fängt schon an mit dem extends ArrayList, und der Kommentar dazu macht es nicht besser, sondern gang im Gegenteil schlimmer.

Dann die TestKlasse:
  • die Namen sind schlecht
  • L1 folgt nicht den Konventionen
  • Random in Tests benutzten ist schlecht
  • die vier leeren Methoden sind überflüssig
  • die Argumente für assertEquals sind vertauscht
  • die Assertions haben keine Message. Entweder Message oder vernünftige Assertions nutzen
  • die Tests selbst sind zu einem großen sehr schlecht geschrieben, am offensichtlichsten testRetainAll
 
L

LimDul

Top Contributor
Dann mach ich mir mal den Spaß ein Review auf den Test-Code:

Java:
import static org.junit.Assert.*;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Random;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

public class MyIntListTest {
// Umbennen, weder sprechend, außerdem widerspricht es den Code-Conventions
	private final MyIntList L1 = new MyIntList();

// Methode ist überflüssig
	@BeforeClass
	public static void setUpBeforeClass() throws Exception {
	}

// Methode ist überflüssig
	@AfterClass
	public static void tearDownAfterClass() throws Exception {
	}

// Methode ist überflüssig
	@Before
	public void setUp() throws Exception {
	}

// Methode ist überflüssig
	@After
	public void tearDown() throws Exception {
	}

// Warum public?
	public List<Integer> createRandomTestListWithAtLeast4Elements() {
// Random will man nicht im Test haben
		Random random = new Random();
		List<Integer> l2 = new ArrayList<Integer>();
		for (int i = 0; i < 4; i++) {
			l2.add(random.nextInt(10));
		}
// Was macht das?
		while (random.nextFloat() <= 0.8706f) {
			l2.add(random.nextInt(10));
		}
		return l2;
	}

	@Test
	public void testSize() {
		assertEquals(L1.size(), 0);
// Random Liste ist ganz schlecht, im Fehlerfall kann man es nicht nachvollziehen
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		L1.addAll(l2);
		assertEquals(L1.size(), l2.size());
// Clear ist überflüssig
		L1.clear(); 
	}

	@Test
	public void testIsEmpty() {
		assertEquals(L1.isEmpty(), true);
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		L1.addAll(l2);
		assertEquals(L1.isEmpty(), false);
		L1.clear();
	}

	@Test
	public void testContains() {
		assertEquals(L1.contains(42), false);
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		L1.addAll(l2);
// Absolut nicht nachvollziehbar im Fehlerfall
		assertEquals(L1.contains(l2.get(1)), true);
		L1.clear();
	}

	@Test
	public void testIterator() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		L1.addAll(l2);
		Iterator<Integer> iterator1 = L1.iterator();
		Iterator<Integer> iterator2 = l2.iterator();
		while (iterator1.hasNext()) {
			assertEquals(iterator2.hasNext(), true);
			assertEquals(iterator1.next(), iterator2.next());
		}
		L1.clear();
	}

	@Test
	public void testToArray() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		L1.addAll(l2);
		Object[] array1 = L1.toArray();
		Object[] array2 = l2.toArray();
		assertArrayEquals(array1, array2);
		L1.clear();
	}

	@Test
	public void testToArrayTArray() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		L1.addAll(l2);
		@SuppressWarnings("unused")
		Exception exception = assertThrows(ArrayStoreException.class, () -> L1.toArray(new String[0]));
		Integer[] array1 = L1.toArray(new Integer[0]);
		Integer[] array2 = l2.toArray(new Integer[0]);
		assertArrayEquals(array1, array2);
		L1.clear();
	}

	@Test
	public void testAddInteger() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		L1.add(l2.get(0));
		assertEquals(L1.get(0), l2.get(0));
		L1.clear();
	}

	@Test
	public void testRemoveObject() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		L1.addAll(l2);
		assertEquals(L1.remove((Object) l2.get(1)), true);
		L1.clear();
	}

	@Test
	public void testContainsAll() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		L1.addAll(l2);
		assertEquals(L1.containsAll(l2), true);
		assertEquals(l2.containsAll(L1), true);
		L1.clear();
	}

	@Test
	public void testAddAllCollectionInteger() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		L1.addAll((Collection<Integer>) l2);
		assertEquals(L1.containsAll(l2), true);
		assertEquals(l2.containsAll(L1), true);
		L1.clear();
	}

	@Test
	public void testRemoveAll() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		L1.addAll(l2);
		L1.removeAll(l2);
		assertEquals(L1.size(), 0);
	}

	@Test
	public void testRetainAll() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		L1.addAll(l2);
		L1.retainAll(l2);
		assertEquals(L1.size(), l2.size());
		L1.clear();
	}

	@Test
	public void testClear() {
		assertEquals(L1.size(), 0);
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		L1.addAll(l2);
		assertNotEquals(L1.size(), 0);
		L1.clear();
		assertEquals(L1.size(), 0);
	}

	@Test
	public void testGet() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		L1.addAll(l2);
		for (int i = 0; i < L1.size(); i++) {
			assertEquals(L1.get(i), l2.get(i));
		}
		L1.clear();
	}

	@Test
	public void testSet() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		L1.addAll(l2);
		assertEquals(L1.set(1, 42), l2.get(1));
		assertEquals(L1.get(1), Integer.valueOf(42));
		L1.clear();
	}

	@Test
	public void testAddIntInteger() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		L1.addAll(l2);
		L1.add(1, 42);
		assertEquals(L1.get(1), Integer.valueOf(42));
		L1.clear();
	}

	@Test
	public void testRemoveInt() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		L1.addAll(l2);
		assertEquals(L1.remove(1), l2.get(1));
		assertNotEquals(L1.get(1), Integer.valueOf(42));
		L1.clear();
	}

	@Test
	public void testIndexOf() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		L1.addAll(l2);
		assertEquals(L1.indexOf(L1.get(1)), l2.indexOf(l2.get(1)));
		assertEquals(L1.indexOf(42), -1);
		L1.clear();
	}

	@Test
	public void testLastIndexOf() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		L1.addAll(l2);
		assertEquals(L1.lastIndexOf(L1.get(1)), l2.lastIndexOf(l2.get(1)));
		assertEquals(L1.lastIndexOf(42), -1);
		L1.clear();
	}
}
// Fehlende Testfälle sind alle Grenzfälle mit IndexOutOfBoundsExeptions. null als Element
// Anstelle von einer Random Liste als Vergleichsliste eine fest definierte Liste nehmen, in den meisten Tests reicht es auch einzelne Elemente hinzuzufügen anstelle mit einer Vergleichsliste zu arbeiten.
 
B

BestGoalkeeper

Bekanntes Mitglied
Version 2:
Java:
import static org.junit.Assert.*;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Random;

import org.junit.Test;

public class MyIntListTest {
	private final MyIntList INITIAL_LIST_TO_TEST = new MyIntList();

	private List<Integer> createRandomTestListWithAtLeast4Elements() {
		Random random = new Random(0);
		List<Integer> list = new ArrayList<Integer>();
		for (int i = 0; i < 4; i++) {
			list.add(random.nextInt(10));
		}
		while (random.nextFloat() <= 0.8706f) {
			list.add(random.nextInt(10));
		}
		return list;
	}

	@Test
	public void testSize() {
		assertEquals(0, INITIAL_LIST_TO_TEST.size());
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		INITIAL_LIST_TO_TEST.addAll(l2);
		assertEquals(l2.size(), INITIAL_LIST_TO_TEST.size());
		INITIAL_LIST_TO_TEST.clear();
	}

	@Test
	public void testIsEmpty() {
		assertEquals(true, INITIAL_LIST_TO_TEST.isEmpty());
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		INITIAL_LIST_TO_TEST.addAll(l2);
		assertEquals(false, INITIAL_LIST_TO_TEST.isEmpty());
		INITIAL_LIST_TO_TEST.clear();
	}

	@Test
	public void testContains() {
		assertEquals(false, INITIAL_LIST_TO_TEST.contains(42));
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		INITIAL_LIST_TO_TEST.addAll(l2);
		assertEquals(true, INITIAL_LIST_TO_TEST.contains(l2.get(1)));
		INITIAL_LIST_TO_TEST.clear();
	}

	@Test
	public void testIterator() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		INITIAL_LIST_TO_TEST.addAll(l2);
		Iterator<Integer> iterator1 = INITIAL_LIST_TO_TEST.iterator();
		Iterator<Integer> iterator2 = l2.iterator();
		while (iterator2.hasNext()) {
			assertEquals(true, iterator1.hasNext());
			assertEquals(iterator2.next(), iterator1.next());
		}
		INITIAL_LIST_TO_TEST.clear();
	}

	@Test
	public void testToArray() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		INITIAL_LIST_TO_TEST.addAll(l2);
		Object[] array1 = INITIAL_LIST_TO_TEST.toArray();
		Object[] array2 = l2.toArray();
		assertArrayEquals(array2, array1);
		INITIAL_LIST_TO_TEST.clear();
	}

	@Test
	public void testToArrayTArray() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		INITIAL_LIST_TO_TEST.addAll(l2);
		@SuppressWarnings("unused")
		Exception exception = assertThrows(ArrayStoreException.class,
				() -> INITIAL_LIST_TO_TEST.toArray(new String[0]));
		Integer[] array1 = INITIAL_LIST_TO_TEST.toArray(new Integer[0]);
		Integer[] array2 = l2.toArray(new Integer[0]);
		assertArrayEquals(array2, array1);
		INITIAL_LIST_TO_TEST.clear();
	}

	@Test
	public void testAddInteger() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		INITIAL_LIST_TO_TEST.add(l2.get(0));
		assertEquals(l2.get(0), INITIAL_LIST_TO_TEST.get(0));
		INITIAL_LIST_TO_TEST.clear();
	}

	@Test
	public void testRemoveObject() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		INITIAL_LIST_TO_TEST.addAll(l2);
		assertEquals(true, INITIAL_LIST_TO_TEST.remove((Object) l2.get(1)));
		INITIAL_LIST_TO_TEST.clear();
	}

	@Test
	public void testContainsAll() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		INITIAL_LIST_TO_TEST.addAll(l2);
		assertEquals(true, INITIAL_LIST_TO_TEST.containsAll(l2));
		assertEquals(true, l2.containsAll(INITIAL_LIST_TO_TEST));
		INITIAL_LIST_TO_TEST.clear();
	}

	@Test
	public void testAddAllCollectionInteger() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		INITIAL_LIST_TO_TEST.addAll((Collection<Integer>) l2);
		assertEquals(true, INITIAL_LIST_TO_TEST.containsAll(l2));
		assertEquals(true, l2.containsAll(INITIAL_LIST_TO_TEST));
		INITIAL_LIST_TO_TEST.clear();
	}

	@Test
	public void testRemoveAll() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		INITIAL_LIST_TO_TEST.addAll(l2);
		INITIAL_LIST_TO_TEST.removeAll(l2);
		assertEquals(0, INITIAL_LIST_TO_TEST.size());
	}

	@Test
	public void testRetainAll() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		INITIAL_LIST_TO_TEST.addAll(l2);
		INITIAL_LIST_TO_TEST.retainAll(l2);
		assertEquals(l2.size(), INITIAL_LIST_TO_TEST.size());
		INITIAL_LIST_TO_TEST.clear();
	}

	@Test
	public void testClear() {
		assertEquals(0, INITIAL_LIST_TO_TEST.size());
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		INITIAL_LIST_TO_TEST.addAll(l2);
		assertNotEquals(0, INITIAL_LIST_TO_TEST.size());
		INITIAL_LIST_TO_TEST.clear();
		assertEquals(0, INITIAL_LIST_TO_TEST.size());
	}

	@Test
	public void testGet() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		INITIAL_LIST_TO_TEST.addAll(l2);
		for (int i = 0; i < INITIAL_LIST_TO_TEST.size(); i++) {
			assertEquals(l2.get(i), INITIAL_LIST_TO_TEST.get(i));
		}
		INITIAL_LIST_TO_TEST.clear();
	}

	@Test
	public void testSet() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		INITIAL_LIST_TO_TEST.addAll(l2);
		assertEquals(l2.get(1), INITIAL_LIST_TO_TEST.set(1, 42));
		assertEquals(Integer.valueOf(42), INITIAL_LIST_TO_TEST.get(1));
		INITIAL_LIST_TO_TEST.clear();
	}

	@Test
	public void testAddIntInteger() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		INITIAL_LIST_TO_TEST.addAll(l2);
		INITIAL_LIST_TO_TEST.add(1, 42);
		assertEquals(Integer.valueOf(42), INITIAL_LIST_TO_TEST.get(1));
		INITIAL_LIST_TO_TEST.clear();
	}

	@Test
	public void testRemoveInt() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		INITIAL_LIST_TO_TEST.addAll(l2);
		assertEquals(l2.get(1), INITIAL_LIST_TO_TEST.remove(1));
		assertNotEquals(Integer.valueOf(42), INITIAL_LIST_TO_TEST.get(1));
		INITIAL_LIST_TO_TEST.clear();
	}

	@Test
	public void testIndexOf() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		INITIAL_LIST_TO_TEST.addAll(l2);
		assertEquals(l2.indexOf(l2.get(1)), INITIAL_LIST_TO_TEST.indexOf(INITIAL_LIST_TO_TEST.get(1)));
		assertEquals(-1, INITIAL_LIST_TO_TEST.indexOf(42));
		INITIAL_LIST_TO_TEST.clear();
	}

	@Test
	public void testLastIndexOf() {
		List<Integer> l2 = createRandomTestListWithAtLeast4Elements();
		INITIAL_LIST_TO_TEST.addAll(l2);
		assertEquals(l2.lastIndexOf(l2.get(1)), INITIAL_LIST_TO_TEST.lastIndexOf(INITIAL_LIST_TO_TEST.get(1)));
		assertEquals(-1, INITIAL_LIST_TO_TEST.lastIndexOf(42));
		INITIAL_LIST_TO_TEST.clear();
	}
}
besser?

die Tests selbst sind zu einem großen sehr schlecht geschrieben
Kannst du vielleicht sagen, was genau du damit meinst?
 
W

White_Fox

Top Contributor
Und ich wünsche mir ein Tutorial über Gradle...ich fände es klasse, wenn sich da mal jemand richtig austoben würde. Der Umfang von mihe7s Tutorial und die Erklärtiefe sind da ein guter Maßstab.
 
B

BestGoalkeeper

Bekanntes Mitglied
BTW. Falls euch das mal passieren sollte, dass ihr die Argumente komplett vertauscht habt, könnt ihr in Eclipse mit dem regex
assertEquals\((.*),(.*)\);
suchen - und mit
assertEquals(\2,\1);
ersetzen.
 
B

BestGoalkeeper

Bekanntes Mitglied
Es fügt zu einer Liste mit 4 Zufallszahlen beliebig viele Zufallszahlen hinzu, aber mit dem Erwartungswert von 5 Zufallszahlen. Also wird die Rückgabeliste in den meisten Fällen 4+5=9 Zahlen (von 0 bis 9 Werten) beinhalten.

Da ich den Seed jetzt auf 0 gesetzt habe, wird immer die gleiche Liste erzeugt...
 
mrBrown

mrBrown

Super-Moderator
Mitarbeiter
Kannst du vielleicht sagen, was genau du damit meinst?
einmal ist das eine Zusammenfassung der anderen Punkte. Random sollte ganz raus, stattdessen einfach [1,3,2,4] nutzen und fertig. Dann sollten die Assertions Messages bekommen, mindestens die, bei denen Schleifen benutzt werden, ein „7 ist nicht gleich 4“ hilft einem da nicht weiter. Dann sollte das clear in jedem Test raus, sowas sollte wenn in @AfterEach zu finden sein, ist hier aber überflüssig. Die Liste kann dann auch einfach in der Methode selbst deklariert werden, das ist kein gemeinsamer Zustand.

Und dann ganz konkret zB testRetainAll, wenn retainAll nichts machen würde, würde der Test trotzdem fehlschlagen. Kannst im Test einfach mal die Zeile mit retainAll entfernen, der Test wird trotzdem nicht fehlschlagen.
Dann testIndexOf, durch die verschachtelten Audrücke blickt niemand durch. Nimm einfach eine Feste Liste, dann steht da nur noch sowas wie assertEquals(3, listUnderTest.indexOf(5)).
Oder auch testRemoveInt, wenn remove einfach nur zwei Indizes vertauschen würde, wäre es erfolgreich. Theoretisch könnte remove aktuell die Liste shuffeln, und der Test würde es nicht merken.
Außerdem testest du aktuell nur den „Happy Path“, wenn ich nichts übersehe.



Ein möglicher Weg für ein Refactoring:
  1. die Liste unter Test inlinen und clean entfernen
  2. die zufällige Liste durch ein List.of(1,3,2,4) ersetzen
  3. dann können alle Aufrufe darauf durch die entsprechenden Konstanten ersetzt werden (zB in l2.lastIndexOf(l2.get(1)))
  4. dann alle verschachtelten Aufrufe entfernen, wenn möglich durch Konstanten ersetzen, ansonsten einfach eine Variable dafür definieren
  5. für jeden Test Vor- und Nachbedingungen noch mal gesondert klarmachen, und überprüfen dass der Test auch genau das sicherstellt (zB für die angesprochenen retainAll und remove)
 
mihe7

mihe7

Top Contributor
Ich finde die Idee eines TDD-Tutorials ganz gut, glaube aber, dass da ein Video besser geeignet wäre. Da dürfte sehr viel Text notwendig sein, um die um 180° gedrehte Denkweise auch wirklich deutlich zu machen; glaub ich.
 
mrBrown

mrBrown

Super-Moderator
Mitarbeiter
Ich finde die Idee eines TDD-Tutorials ganz gut, glaube aber, dass da ein Video besser geeignet wäre. Da dürfte sehr viel Text notwendig sein, um die um 180° gedrehte Denkweise auch wirklich deutlich zu machen; glaub ich.
Es geht, ich hab mal eine (wenn auch grobe) Einführung in TDD mit FizzBuzz geschrieben, die könnte ich eigentlich mal überarbeiten und hier rein Stellen
 
L

LimDul

Top Contributor
Das würde mich auch mal reizen, darüber zu diskutieren. Ich werde mit TDD auch nicht warm. Ich werfe bei komplexeren Dinge teilweise die API ein paar mal über den Haufen bis ich eine habe, die tragfähig ist. Da jedes mal Tests mitzuschleifen und zu refaktoren fühlt sich nach Overhead an. Ich nehme für mich in Anspruch beim Design der API auf Testfähigkeit zu achten und nehme dann lieber Refaktoring Aufwand in Kauf, die API testfähig zu machen als dauernd die Tests zu refaktoren. Für mich fühlt sich Test-Driven - außer in akademischen oder Mini-Beispielen - nach mehr Overhead als nutzen an.

PS: Bezüglich der Asserts bevorzuge ich Junit 5 + Hamcrest mit asserThat(wert, is(erwartet)). Da finde ich den Test lesbarer als das assertEquals und ich bekomme direkt sinnvolle Fehlertexte generiert. Aber klar, das ist wieder eine Extra-Lib, die man nicht unbedingt sofort einführen will.
 
mrBrown

mrBrown

Super-Moderator
Mitarbeiter
Das würde mich auch mal reizen, darüber zu diskutieren. Ich werde mit TDD auch nicht warm. Ich werfe bei komplexeren Dinge teilweise die API ein paar mal über den Haufen bis ich eine habe, die tragfähig ist. Da jedes mal Tests mitzuschleifen und zu refaktoren fühlt sich nach Overhead an. Ich nehme für mich in Anspruch beim Design der API auf Testfähigkeit zu achten und nehme dann lieber Refaktoring Aufwand in Kauf, die API testfähig zu machen als dauernd die Tests zu refaktoren. Für mich fühlt sich Test-Driven - außer in akademischen oder Mini-Beispielen - nach mehr Overhead als nutzen an.
Zu dieser tragfähigen API kommen ist ja grad das Ziel von TDD, dass man da durchaus mal Dinge ändern muss ist durchaus erwünscht.
Bei dir entsteht die Schittstelle ja sicherlich auch nicht "aus dem Nichts", entweder ist die direkt an den "richtigen" Code angebunden oder du spielst das detailliert genug im Kopf durch – vermuten würde ich da letzteres, sonst hättest du das Problem mit Refactoring ja trotzdem?

PS: Bezüglich der Asserts bevorzuge ich Junit 5 + Hamcrest mit asserThat(wert, is(erwartet)).
Statt Hamcrest bevorzuge ich AssertJ mit asserThat(wert).isEqualTo(erwartet), grad auch wegen der "Discoverability" durch die IDE.
 
mihe7

mihe7

Top Contributor
Es geht, ich hab mal eine (wenn auch grobe) Einführung in TDD mit FizzBuzz geschrieben, die könnte ich eigentlich mal überarbeiten und hier rein Stellen
Reinstellen! Jetzt! :)

Das würde mich auch mal reizen, darüber zu diskutieren.
Unsere Anwendungen sind so langweilig, dass sich da zu großen Teilen überhaupt kein Test rentiert (das meiste ist einfach CRUD -> Getter/Setter/JPA). Ich wüsste gar nicht, was, wie und warum ich da etwas testen sollte.

Allerdings gibt es einige interessantere Klassen, wo etwas Logik drinsteckt. Bei diesen habe ich durchaus auch TDD angewendet und bin zu 100 % davon überzeugt, dass es dort einiges gebracht hat. Auch bin ich immer wieder erstaunt, in welcher Geschwindigkeit und wie stark sich die Denkweise ändert, sobald man mit diesem Ansatz an die Sache herangeht.
 
mihe7

mihe7

Top Contributor
Und ich wünsche mir ein Tutorial über Gradle...ich fände es klasse, wenn sich da mal jemand richtig austoben würde. Der Umfang von mihe7s Tutorial und die Erklärtiefe sind da ein guter Maßstab.
Du musst einfach nur genügend Fragen stellen und ggf. auf idiotische Youtube-Videos verweisen, bis es jemandem zu blöd wird und den Spaß mal zusammenfasst :p

Tatsächlich war das die Intention hinter den beiden Threads. Wenn hier alle drei Wochen jemand mit einem YT-Pong-like-Code (u. ä.) aufschlägt, dann nervt es, immer und immer wieder das gleiche zu schreiben. Außerdem bin ich bzgl. des Videos immer am Lästern, dann muss man auch mal zeigen, dass es auch anders geht :) Hinzu kommt, dass es daneben auch noch Leute gibt, die sagen: hey, ich hab mir Java-Bücher besorgt und den Spaß gelernt, aber irgendwie fehlt der rote Faden, wie man ein Projekt von vorne bis hinten angehen kann. Auch dafür kann der Beitrag IMO bis zu einem gewissen Grad herhalten. Das Spiel selbst ist ja eigentlich Nebensache - dafür bin ich eigentlich gar nicht geeignet, weil ich normalerweise keine Spiele programmiere.

Hinter dem Beitrag über Arrays steckt eine ähnliche Motivation, aber in wesentlich kleinerem Maßstab. Verkettete Listen wären auch noch so ein Thema, wo es regelmäßig Verständnisprobleme gibt.
 
T

temi

Top Contributor
Tatsächlich war das die Intention hinter den beiden Threads. Wenn hier alle drei Wochen jemand mit einem YT-Pong-like-Code (u. ä.) aufschlägt, dann nervt es, immer und immer wieder das gleiche zu schreiben.
Wäre allerdings nicht schlecht, wenn man diese Art von Themen irgendwo an einer festen Stelle wiederfinden könnte. Das denke ich mir auch immer, wenn zum x-ten Mal erklärt wird, was "static" bedeutet oder vieles mehr.

Ich weiß dann zwar, dass es eine gute Antwort hier im Forum gab, aber finde sie meist nicht wieder.
 
B

BestGoalkeeper

Bekanntes Mitglied
Version 3 (jetzt sollte es etwas übersichtlicher sein):
Java:
import static org.junit.Assert.*;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.junit.Test;

public class MyIntListTest {
	private MyIntList createTestList() {
		MyIntList testList = new MyIntList();
		return testList;
	}

	private void addElementsTo(MyIntList testList) {
		testList.add(4);
		testList.add(5);
		testList.add(3);
		testList.add(2);
		testList.add(1);
	}

	@Test
	public void testSize() {
		MyIntList testList = createTestList();
		assertEquals(0, testList.size());
		addElementsTo(testList);
		assertEquals(5, testList.size());
	}

	@Test
	public void testIsEmpty() {
		MyIntList testList = createTestList();
		assertEquals(true, testList.isEmpty());
		addElementsTo(testList);
		assertEquals(false, testList.isEmpty());
	}

	@Test
	public void testContains() {
		MyIntList testList = createTestList();
		assertEquals(false, testList.contains(null));
		assertEquals(false, testList.contains(42));
		addElementsTo(testList);
		assertEquals(true, testList.contains(1));
	}

	@Test
	public void testIterator() {
		MyIntList testList = createTestList();
		Iterator<Integer> iterator = testList.iterator();
		assertEquals(false, iterator.hasNext());
		addElementsTo(testList);
		iterator = testList.iterator();
		assertEquals(true, iterator.hasNext());
		assertEquals(Integer.valueOf(4), iterator.next());
		assertEquals(true, iterator.hasNext());
		assertEquals(Integer.valueOf(5), iterator.next());
		assertEquals(true, iterator.hasNext());
		assertEquals(Integer.valueOf(3), iterator.next());
		assertEquals(true, iterator.hasNext());
		assertEquals(Integer.valueOf(2), iterator.next());
		assertEquals(true, iterator.hasNext());
		assertEquals(Integer.valueOf(1), iterator.next());
		assertEquals(false, iterator.hasNext());
	}

	@Test
	public void testToArray() {
		MyIntList testList = createTestList();
		addElementsTo(testList);
		Object[] array1 = testList.toArray();
		Object[] array2 = { 4, 5, 3, 2, 1 };
		assertArrayEquals(array2, array1);
	}

	@Test
	public void testToArrayTArray() {
		MyIntList testList = createTestList();
		addElementsTo(testList);
		@SuppressWarnings("unused")
		Exception exception = assertThrows(ArrayStoreException.class, () -> testList.toArray(new String[0]));
		Integer[] array1 = testList.toArray(new Integer[0]);
		Integer[] array2 = { 4, 5, 3, 2, 1 };
		assertArrayEquals(array2, array1);
	}

	@Test
	public void testAddInteger() {
		MyIntList testList = createTestList();
		assertEquals(true, testList.add(5));
		assertEquals(true, testList.add(null));
		assertEquals(Integer.valueOf(5), testList.get(0));
	}

	@Test
	public void testRemoveObject() {
		MyIntList testList = createTestList();
		assertEquals(false, testList.remove((Object) 1));
		addElementsTo(testList);
		assertEquals(true, testList.remove((Object) 1));
		assertEquals(false, testList.remove((Object) 42));
	}

	@Test
	public void testContainsAll() {
		MyIntList testList = createTestList();
		addElementsTo(testList);
		assertEquals(true, testList.containsAll(List.of(1, 2, 3)));
		assertEquals(true, testList.containsAll(List.of(4, 5, 3, 2, 1)));
		assertEquals(false, testList.containsAll(List.of(1, 6)));
	}

	@Test
	public void testAddAllCollectionInteger() {
		MyIntList testList = createTestList();
		assertEquals(true, testList.addAll((Collection<Integer>) List.of(1, 6)));
		assertEquals(Integer.valueOf(1), testList.get(0));
		assertEquals(Integer.valueOf(6), testList.get(1));
	}

	@Test
	public void testRemoveAll() {
		MyIntList testList = createTestList();
		addElementsTo(testList);
		assertEquals(true, testList.removeAll(List.of(4, 5, 3, 2)));
		assertEquals(Integer.valueOf(1), testList.get(0));
		assertEquals(1, testList.size());
	}

	@Test
	public void testRetainAll() {
		MyIntList testList = createTestList();
		addElementsTo(testList);
		assertEquals(true, testList.retainAll(List.of(1, 6)));
		assertEquals(Integer.valueOf(1), testList.get(0));

		assertEquals(1, testList.size());
	}

	@Test
	public void testClear() {
		MyIntList testList = createTestList();
		assertEquals(0, testList.size());
		addElementsTo(testList);
		assertEquals(5, testList.size());
		testList.clear();
		assertEquals(0, testList.size());
	}

	@Test
	public void testGet() {
		MyIntList testList = createTestList();
		addElementsTo(testList);
		assertEquals(Integer.valueOf(4), testList.get(0));
		assertEquals(Integer.valueOf(5), testList.get(1));
		assertEquals(Integer.valueOf(3), testList.get(2));
		assertEquals(Integer.valueOf(2), testList.get(3));
		assertEquals(Integer.valueOf(1), testList.get(4));
		@SuppressWarnings("unused")
		Exception exception = assertThrows(IndexOutOfBoundsException.class, () -> testList.get(5));
	}

	@Test
	public void testSet() {
		MyIntList testList = createTestList();
		addElementsTo(testList);
		assertEquals(Integer.valueOf(5), testList.set(1, 42));
		assertEquals(Integer.valueOf(42), testList.get(1));
	}

	@Test
	public void testAddIntInteger() {
		MyIntList testList = createTestList();
		addElementsTo(testList);
		assertEquals(true, testList.add(42));
		assertEquals(Integer.valueOf(42), testList.get(5));
	}

	@Test
	public void testRemoveInt() {
		MyIntList testList = createTestList();
		addElementsTo(testList);
		assertEquals(Integer.valueOf(5), testList.remove(1));
		assertEquals(Integer.valueOf(3), testList.get(1));
	}

	@Test
	public void testIndexOf() {
		MyIntList testList = createTestList();
		addElementsTo(testList);
		assertEquals(4, testList.indexOf(1));
		assertEquals(1, testList.indexOf(5));
	}

	@Test
	public void testLastIndexOf() {
		MyIntList testList = createTestList();
		addElementsTo(testList);
		assertEquals(4, testList.lastIndexOf(1));
		assertEquals(1, testList.lastIndexOf(5));
	}
}

Leider ist mein subjektiver Eindruck dass man genau so viel Zeit für das Schreiben der Tests und das Schreiben der Liste verwendet.
 
L

LimDul

Top Contributor
Zu dieser tragfähigen API kommen ist ja grad das Ziel von TDD, dass man da durchaus mal Dinge ändern muss ist durchaus erwünscht.
Bei dir entsteht die Schittstelle ja sicherlich auch nicht "aus dem Nichts", entweder ist die direkt an den "richtigen" Code angebunden oder du spielst das detailliert genug im Kopf durch – vermuten würde ich da letzteres, sonst hättest du das Problem mit Refactoring ja trotzdem?
Es ist schwer da Beispiele zu geben, weil entweder ist es so "simpel" das der TDD da weder große Vor- noch Nachteile bringt oder es ist so komplex, dass man es nicht ohne groß die Fachlichkeit zu erklären, hier darstellen kann. Ich versuche es trotzdem mal.

Um ein Feature umzusetzen muss ich:
* Modellklassen schreiben, die haben Atribute, getter/setter und ein paar kleinere Logiken um sich selbst zu validieren etc.
* Ich brauche Business-Logik, die mit diesen Modellklassen Dinge tut und transformiert.
* Ich brauche ein UI um das Ergebnis anzuzeigen. (In dem Fall aber ausgeklammert, weil der Rest schon komplex genug ist)

Mein Vorgehen war:
* Ich lege die Modellklassen an (keine Tests)
* Ich fange die Businesslogik an
** Ich stelle fest, um die Logik umzusetzen, ist es sinnvoll an den Interfaces der Modellklassen und den Ableitungen ein einigen Stellen was zu ändern. Interfaces einziehen, Code ändern etc.
** Es entsteht Refaktoring Aufwand an den Modellklassen
* Dies wiederholt sich ggf. ein paar mal, je weiter die Businesslogik fortschreitet
* Am Ende hab ich eine hoffentlich funktionierende Businesslogik und Modell-Klassen die darauf ausgerichtet sind
* Dann schreibe ich entsprechende Unit-Tests auf allen Ebenen
* Ggf. wird noch mal Refaktoring betrieben um an schlechter testbare Methoden sinnvoll ranzukommen
* Teilweise werden (insbesondere im Bezug auf Rand- und Sonderfälle) noch Fehler gefunden und korrigiert.

Mache ich das nun test-driven, sehe ich aktuell nicht, wie ich mein Modell direkt hinbekomme so dass es für die Businesslogik passt. Ich schreibe dann zwar direkt Tests für die Modellklassen, die ich dann aber in den Refaktoring Schritten, die kommen, mit Refaktoren muss bzw. teilweise auch einfach wegschmeissen kann, weil sie überflüssig oder schlicht durch die Umstellung falsch sind.

Anderes Problem ist, mir widerstrebt der Ansatz nur Code zu schreiben, der die Tests erfüllt, ich will direkt die ganzen Anforderungen erfüllen.

Allerdings gibt es einige interessantere Klassen, wo etwas Logik drinsteckt. Bei diesen habe ich durchaus auch TDD angewendet und bin zu 100 % davon überzeugt, dass es dort einiges gebracht hat. Auch bin ich immer wieder erstaunt, in welcher Geschwindigkeit und wie stark sich die Denkweise ändert, sobald man mit diesem Ansatz an die Sache herangeht.
Ich glaube das ist durchaus viel wahres bei.

Ich habe da Gefühl, es würde mir nix bringen, also mach ich es nicht. Ich bin "so gut", dass ich das nicht brauche. Ich glaube durchaus, dass diese Einschätzung eher falsch ist und es durchaus was bringen würde. Aber es fehlt die Motivation bzw. der Leuchtturm am Ende das mal wirklich im Projekt-Alltag konsequent auszuprobieren.


Leider ist mein subjektiver Eindruck dass man genau so viel Zeit für das Schreiben der Tests und das Schreiben der Liste verwendet.
Ja und? Ich sehe da das Problem nicht. Ich hab Stellen, da gilt. Business Code: 250 Zeilen. Test-Klasse: 1000 Zeilen. Ich finde es vollkommen in Ordnung, dass man für Tests schreiben genauso lange braucht wie für die eigentliche Fachlichkeit.
 
kneitzel

kneitzel

Top Contributor
Leider ist mein subjektiver Eindruck dass man genau so viel Zeit für das Schreiben der Tests und das Schreiben der Liste verwendet.

Wo ist das Problem, wenn es so wäre? Ohne Unit Tests kann man triviale Schulprobleme schneller lösen oder was wäre da die Beschreibung des Problems?

Bei der Software Entwicklung geht es halt nicht um kleine Schulprobleme sondern es geht um komplexe Aufgaben. Und d ist es existenziell, dass der Code eine gute Testabdeckung hat, denn ohne ist es höchst problematisch wenn nicht gar unmöglich, Änderungen durchzuführen.

@LimDul: Dein Vorgehen kenne ich auch zu gut. Aber das Problem ist da dann sehr oft, dass die Testabdeckung eben nicht wirklich gut ist. Alleine schon, dass du Code schreibst ohne Unit Tests kann dazu führen, dass der Code nicht wirklich gut Test Ar ist.

Und du hast generell das Problem, dass Du ungeteilter Code schreibst und damit Refactorings / Änderungen kritisch sind. Also entweder ist die Komplexität so, dass es überschaubar bleibt - dann sollte aber auch ein vernünftiges Design problemlos vorab möglich sein. Oder es ist so komplex, dass du bei Deinen Anpassungen in Probleme rennst - die Lösung ist hier aber nicht, TDD zu verwenden, sondern die Problematik von Anfang an mehr agil anzugehen. Also die Projektplanung zu verändern, bessere Arbeitspakete schnüren u.s.w.

Wobei ich zugeben muss, dass es auch bei mir nicht wirklich mit TDD abläuft. Derzeit darf ich an etwas schreiben, das dies sehr erschwert. Aber das merke ich dann auch an entsprechenden Problemen bei der Entwicklung und ich vermisse da einiges.
 
L

LimDul

Top Contributor
@LimDul: Dein Vorgehen kenne ich auch zu gut. Aber das Problem ist da dann sehr oft, dass die Testabdeckung eben nicht wirklich gut ist. Alleine schon, dass du Code schreibst ohne Unit Tests kann dazu führen, dass der Code nicht wirklich gut Test Ar ist.
Und du hast generell das Problem, dass Du ungeteilter Code schreibst und damit Refactorings / Änderungen kritisch sind. Also entweder ist die Komplexität so, dass es überschaubar bleibt - dann sollte aber auch ein vernünftiges Design problemlos vorab möglich sein. Oder es ist so komplex, dass du bei Deinen Anpassungen in Probleme rennst - die Lösung ist hier aber nicht, TDD zu verwenden, sondern die Problematik von Anfang an mehr agil anzugehen. Also die Projektplanung zu verändern, bessere Arbeitspakete schnüren u.s.w.

Wobei ich zugeben muss, dass es auch bei mir nicht wirklich mit TDD abläuft. Derzeit darf ich an etwas schreiben, das dies sehr erschwert. Aber das merke ich dann auch an entsprechenden Problemen bei der Entwicklung und ich vermisse da einiges.
Es kommt bei uns ja trotzdem testbarer Code raus. Wir haben eine extrem gute Testabdeckung - 80% im nicht generierten Code und an den wichtigen Businessklassen liegt die bei deutlich über 90%. Was hilft ist, dass wir eine tragfähige und gute Architektur haben es entsprechend Patterns gibt wie Dinge umzusetzen sind. Dadurch ist es - auch ohne Test-Driven - relativ gut möglich Testbaren Code zu schreiben. Ich glaube das Test-Driven einen da hin bringt wo wir schon sind. (bzw. vermutlich noch ein gutes Stück weiter - aber da fehlt mir wie gesagt, die Vision) Ein Pluspunkt bei uns die ausgeprägte Review Kultur, Code der nicht getestet ist, kommt nicht durchs Review.

Wenn man irgendwo unterwegs ist, wo Tests als lästiges Übel gesehen werde, dann glaube ich ist Test-Driven-Development ein sehr gutes Vorgehen da mal den Spieß umzukehren und bringt auch sofort was. Aber den Zustand haben wir bei uns nicht. Vielleicht muss ich es doch einfach mal ausprobieren :)
 
T

temi

Top Contributor
Ich bin ja kein Softwareentwickler, aber grundsätzlich ist mir TDD schon bekannt. Würde man das komplett durchziehen, dann beginnt man mit dem Schreiben des Tests und hat erst einmal nicht compilierbaren Code, weil es die entsprechenden Klassen ja gar nicht gibt.
Der erste Schritt wäre es demnach, den Testcode zunächst compilierbar zu machen, um überhaupt einen fehlgeschlagenen Test starten zu können.
Damit würde man ja die Festlegung, was die zu testenden Klassen "können" sollen, direkt beim Schreiben des Tests bestimmen und damit durch den Test die API zu erstellen.

Ist das so gedacht?

Grundsätzlich ist es ja so, dass man seine Schnittstellen so schlank wie möglich gestalten sollte, also keine Methoden einführt, die man "vielleicht irgendwann mal brauchen kann". Der Test würde (zumindest ein Teil davon) die Usecases umsetzen und damit sozusagen "ermitteln", wie die dazu notwendige API aussieht.
 
Zuletzt bearbeitet:
sascha-sphw

sascha-sphw

Bekanntes Mitglied
 
kneitzel

kneitzel

Top Contributor
Ich bin ja kein Softwareentwickler, aber grundsätzlich ist mir TDD schon bekannt. Würde man das komplett durchziehen, dann beginnt man mit dem Schreiben des Tests und hat erst einmal nicht compilierbaren Code, weil es die entsprechenden Klassen ja gar nicht gibt.
Der erste Schritt wäre es demnach, den Testcode zunächst compilierbar zu machen, um überhaupt einen fehlgeschlagenen Test starten zu können.
Damit würde man ja die Festlegung, was die zu testenden Klassen "können" sollen, direkt beim Schreiben des Tests bestimmen und damit durch den Test die API zu erstellen.

Ist das so gedacht?

Grundsätzlich ist es ja so, dass man seine Schnittstellen so schlank wie möglich gestalten sollte, also keine Methoden einführt, die man "vielleicht irgendwann mal brauchen kann". Der Test würde (zumindest ein Teil davon) die Usecases umsetzen und damit sozusagen "ermitteln", wie die dazu notwendige API aussieht.

Wichtig ist, dass du immer nur ganz kleine Dinge machst. Es gibt also nicht mehrere nicht existierende Klassen sondern nur eine. Und der Test umfasst nur eine Funktionalität, also unter dem Strich eine Methode.
 
T

temi

Top Contributor
Wichtig ist, dass du immer nur ganz kleine Dinge machst. Es gibt also nicht mehrere nicht existierende Klassen sondern nur eine.
Ja, das ist richtig. Das war so gemeint, aber vielleicht schlecht ausgedrückt.
Und der Test umfasst nur eine Funktionalität, also unter dem Strich eine Methode.
Auch dieses. Es gibt einen Test (eine Testmethode), die eine Methode der Klasse testet.

Darauf wollte ich aber gar nicht hinaus. Es ging mir eher darum, ob durch den Test das Interface der zu testenden Klasse festgelegt wird?

EDIT: Klingt auch seltsam, ich weiß grad nicht, wie ich es besser ausdrücken soll ;)
 
Zuletzt bearbeitet:
kneitzel

kneitzel

Top Contributor
Das ist der Start der Implementation. Aber die Festlegung erfolgt ja hoffentlich vorab beim Design. Das erfolgt also schon deutlich früher und ist unabhängig von TDD und Co.

Aber evtl. habe ich Dich wieder nicht richtig verstanden. Aber das Design kommt meiner Meinung nach deutlich bevor ich eine Zeile Code schreibe.
 
T

temi

Top Contributor
Das ist der Start der Implementation. Aber die Festlegung erfolgt ja hoffentlich vorab beim Design. Das erfolgt also schon deutlich früher und ist unabhängig von TDD und Co.

Aber das Design kommt meiner Meinung nach deutlich bevor ich eine Zeile Code schreibe.
Das ist wohl sehr sinnvoll. Wie konkret sollte das Design erfolgen? Bis auf den einzelnen Rückgabetyp und die genauen Parameter?
 
mrBrown

mrBrown

Super-Moderator
Mitarbeiter
Das ist der Start der Implementation. Aber die Festlegung erfolgt ja hoffentlich vorab beim Design. Das erfolgt also schon deutlich früher und ist unabhängig von TDD und Co.
Jein, wenn man TDD ganz strikt anwendet, entsteht jede konkrete Methode und jede Klasse nur durch Refactoring. Ein High-Leveliges Design in Form von Domänenmodellen hat man sicherlich vorher, aber konkrete Methoden kommen da ja nicht drin vor, Services etc fehlen auch.


Wichtig ist, dass du immer nur ganz kleine Dinge machst. Es gibt also nicht mehrere nicht existierende Klassen sondern nur eine. Und der Test umfasst nur eine Funktionalität, also unter dem Strich eine Methode.
Können durchaus auch mal mehrere Klassen sein, zB in Form von Parametern und Rückgabetypen. Aber ja, weniger ist besser.
 
T

temi

Top Contributor
Können durchaus auch mal mehrere Klassen sein, zB in Form von Parametern und Rückgabetypen. Aber ja, weniger ist besser.
Ist es demnach einigermaßen wichtig mit den "richtigen" Tests zu beginnen?

Ich kann mir ja aussuchen, ob ich mit einer Klasse beginne, die viele Abhängigkeiten zu anderen Klassen hat oder mit einer Klasse, von der andere abhängen.
 
sascha-sphw

sascha-sphw

Bekanntes Mitglied
Ich kann mir ja aussuchen, ob ich mit einer Klasse beginne, die viele Abhängigkeiten zu anderen Klassen hat oder mit einer Klasse, von der andere abhängen.
Ich handhabe das wie beim Design, von Grob nach Detailliert. Beim Design steht ja auch erstmal nur der Use-Case.
 
kneitzel

kneitzel

Top Contributor
Also wie ein Design erfolgt kann ganz unterschiedlich sein. Angefangen von "Ich dokumentiere alles ganz genau in UML" bis hin zu "Ich mache nur paar Skizzen und dann ist das schon klar.

In erstem Falle ist alles genau vorgegeben. Das entspricht dann aber auch nicht mehr einer agilen Herangehensweise. Ich kenne es eher etwas stark vereinfacht, das dann auch etwas Richtung Scrum geht welches einen agilen Ablauf mit verschiedenen Rollen definiert. Da wird dann nicht wirklich im Detail alles dokumentiert, aber es wird sicher gestellt, dass es ein gemeinsames Verständnis im Team gibt. Also Beispiel von FizzBuzz von @mrBrown: Da ist dann klar, was von der Liste erwartet wird. Aber niemand wird da wohl hin gehen und das lange dokumentieren. Es ist beschrieben worden und dann ist klar, dass da eine Liste von Zahlen, "Fuzz", "Buzz" und "FuzzBuzz" zurück kommen kann.
Aber es wird dann wohl eine Diskussion geben, wie das zu implementieren wird. Ist es so eine einfache Listenerstellung? Oder brauchen wir das deutlich flexibler?
Vielleicht diskutiert das Team dann ein Strategy Pattern. Dann gibt es ein Interface, das eben eine Zahl bekommt und ein boolean zurück gibt mit Implementationen für Buzz und Fuzz.... Die Frage ist ja: Wo brauche ich was? Es dreht sich ja nicht um so eine Liste. Es dreht sich ja immer um eine Applikation, die erstellt werden soll mit irgendwelchen Features. Und dann arbeite ich nur an einem WorkItem.

Red doch nicht so primitiv daher. TDD ist praktisch tot. Ich weiß nicht, ob du das noch nicht wusstest.

Ahh ja. Und wieso kommt von Dir nichts handfestes? Wodurch soll es denn abgelöst werden? Worauf willst Du bitte genau hinaus?
Geht es da um TCR ()? Der die Session aus 2014 https://martinfowler.com/articles/is-tdd-dead/ (mit Kent Beck, Martin Fowler, ...), den facebook Post von Kent (https://www.facebook.com/notes/kent-beck/rip-tdd/750840194948847), ...

Also irgendwie ist Deine Aussage extrem primitiv weil einfach nichts substanzielles enthalten ist.

Jein, wenn man TDD ganz strikt anwendet, entsteht jede konkrete Methode und jede Klasse nur durch Refactoring. Ein High-Leveliges Design in Form von Domänenmodellen hat man sicherlich vorher, aber konkrete Methoden kommen da ja nicht drin vor, Services etc fehlen auch.
Da kriege ich dann etwas Bauchschmerzen. Aber ich muss gestehen, dass ich da die Theorie von Kent Beck nicht im Detail parat habe. Nach meinem aktuellen Verständnis ist das aber einfach etwas, das außerhalb vom TDD liegt. TDD beschreibt nur eine Technik, wie ich testbaren und getesteten Code schreiben kann und diesen Status beibehalten kann. Das Thema Design habe ich da eigentlich nie wirklich enthalten gesehen.

Aber da mag ich dann vielleicht tatsächlich in der Theorie nicht tief genug drin gewesen sein. Aber ein Design kommt immer an erster Stelle und nie irgend ein Code. Du musst ja erst einmal wissen, was Du überhaupt willst / brauchst. Und Du hast da ja hoffentlich gewisse Verfahren, die Du da einsetzt.

Also sozusagen für jeden Teilbereich die entsprechende Lösung.
-> Großes Projekt: Da kommt dann PRINCE2 oder PMI ran. Da wird wohl kaum einer diskutieren wollen, spätestens, wenn es um eine gewisse Größe geht (und dann Geschäftsführer ggf. privat haften müssen wegen grob fahrlässigem Handeln und so ...)
-> Abarbeitung kleiner Bereiche: Da kann dann ein Team mit SCRUM agieren oder was auch immer. Da erlebe ich auch oft genug die Planung mit Excel oder Project mehr nach Waterfall.
-> Umsetzung der einzelnen Tätigkeit: Da kann dann ein Entwickler TDD einsetzen. Da kommen dann aber auch viele andere Dinge rein wie CI, DI, ....

Das ist aber nur eine ganz grobe Sichtweise ohne viele Details welche bestimmt auch recht spezialisiert ist - so große Projekte wird man ja nicht unbedingt haben.... Aber es soll nur etwas aufzeigen: Ich sehe durchaus mehrere Bereiche und die unterscheide ich. Also TDD ist nicht für den Bereich Design da....
 
kneitzel

kneitzel

Top Contributor
Ist es demnach einigermaßen wichtig mit den "richtigen" Tests zu beginnen?

Ich kann mir ja aussuchen, ob ich mit einer Klasse beginne, die viele Abhängigkeiten zu anderen Klassen hat oder mit einer Klasse, von der andere abhängen.

Also auch hier sehe ich erst einmal kein TDD Thema. Wenn Du die (oo) Analyse abgeschlossen hast und das (oo) Design gemacht hast, dann wirst Du Dir die Arbeit ja strukturieren. In Agilen Teams läuft es oft darauf hinaus, dass Du Arbeitspakete (Oft Tasks oder so genannt) definierst. Und diese werden oft auch mit Abhängigkeiten versehen:
Das Arbeitspaket, welches irgendwas mit "Kunden" macht, hat eine Abhängigkeit zu dem Arbeitspaket, dass die Entity "Kunde" erstellen soll.

Somit ist dies aus meiner Sicht nicht eine Frage vom TDD sondern von der Arbeitsorganisation. Wie organisiere ich meine Arbeit, damit ich alle Arbeiten ordentlich abschließen kann.

Baue ich erst das Dach (Super, wenn ich das am Boden baue und später anhebe ... Alleine das Arbeiter nicht herunter fallen können ist doch super!) oder fange ich doch etwas anders an und hebe z.B. erst einmal den Keller aus um den dann zu erstellen? Das ist doch unabhängig von den Arbeitsweisen, die die Qualität sicher stellen soll, also z.B. beim Mauern ein Lot verwenden, damit die Mauer gerade ist.
 
mihe7

mihe7

Top Contributor
TDD beschreibt nur eine Technik, wie ich testbaren und getesteten Code schreiben kann und diesen Status beibehalten kann. Das Thema Design habe ich da eigentlich nie wirklich enthalten gesehen.
Hängen testbarer Code und gutes Design nicht zusammen? Evtl. lässt sich TDD (auch) als Schritt in einem "iterativen Micro-Design" sehen, weil es testbaren Code forciert. Das heißt natürlich nicht, dass TDD Voraussetzung für gutes Design wäre.
 
W

White_Fox

Top Contributor
Einen Test zu schreiben ist doch eigentlich immer eine tolle Möglichkeit, sein Problem mal "auszuprobieren".

Bisher mache ich das immer so, daß ich mir für mein Problem eine Lösung ausdenke, meist ein paar Klassen dazu benötige, diese Klassen schreibe und diesen ein paar Methoden mitgebe (ohne Implementierung, in den Methoden steht dann nur eine Zeile throws UnsupportedOperationException();, dann die Javadocs für die Methoden schreibe, eine Test generiere und implementiere und am Ende werden die Methoden der Klassen implementiert.

Dabei passiert es halt öfter mal, daß ich irgendwann merke daß ich in eine Sackgasse renne und die Schnittstelle umbaue...und damit auch den Test.

Ich werde demnächst mal Folgendes ausprobieren: Im Test erstmal mein Problem nachbauen inkl. der Lösung, die ich mir vorstelle. Sozusagen den Test mißbrauchen, um das Design zu erstellen. Und dann die Implementierung vornehmen.
Mal schauen was dabei herauskommt...
 
H

httpdigest

Top Contributor
Hängen testbarer Code und gutes Design nicht zusammen? Evtl. lässt sich TDD (auch) als Schritt in einem "iterativen Micro-Design" sehen, weil es testbaren Code forciert. Das heißt natürlich nicht, dass TDD Voraussetzung für gutes Design wäre.
Sehe ich hier genauso. Wenn Tests am Anfang definiert werden, dann lenkt das das Design des zu testenden Codes eher in Richtung "wenige Abhängigkeiten", "wenige bzw. eine Verantwortlichkeit", "geringe Komplexität", ...), da das eben auch das Testen einfacher macht.
Wenn man ohne Tests anfängt, ein (Micro-)design zu entwerfen, kommt man schnell zum Punkt, dass Klassen/Methoden überladen werden, viele Verantwortlichkeiten abbilden und viele Abhängigkeiten bekommen. Das sorgt im Zweifel erst beim späteren Schreiben von Tests dazu, dass das Schreiben der Tests eben "keinen Spass macht", weil das Design von vornherein vielen Prinzipien objektorientierter Programmierung (insbesondere S.O.L.I.D.) widersprochen hat und somit auch nicht gut zu testen, nicht gut zu warten und nicht gut zu verstehen ist.
 
kneitzel

kneitzel

Top Contributor
Hängen testbarer Code und gutes Design nicht zusammen? Evtl. lässt sich TDD (auch) als Schritt in einem "iterativen Micro-Design" sehen, weil es testbaren Code forciert. Das heißt natürlich nicht, dass TDD Voraussetzung für gutes Design wäre.
Also gewisse Dinge gehören etwas zusammen. Und auch die Ausführungen von @httpdigest sind durchaus korrekt. Aber dennoch sehe ich Design Entscheidungen von TDD abgekoppelt. Diese werden dann bei TDD explizit umgesetzt (wenn alle Tests erfolgreich sind), aber ich sehe es als einen eigenständigen Prozess an. Design muss man sich überlegen und das, ehe man anfängt, ersten Code zu schreiben. Was für Klassen willst Du denn schreiben, wenn Du Dir das noch nicht überlegt hast? Und der Punkt beim TDD ist eher:
"Die Unit Tests laufen - nun überdenk Dein Design. Gibt es Handlungsbedarf?"

Ein Design muss so aufgebaut sein, dass es dann auch Testbar ist. Ich will halt ein gutes Design haben und da gehört das mit rein. Und wenn ich (mehr oder weniger) nach TDD vorgehe, dann komme ich zu dem Punkt, dass ich etwas entwickle, das Testbar sein muss und ggf. überdenke ich das Design noch einmal. Aber wie gesagt: Irgendwie sehe ich da zwei Dinge, die zwar zusammen gehören, aber dennoch zwei Paar Schuhe sind.

SOLID und Co hat ja mit TDD nicht viel zu tun. TDD will natürlich - wie eigentlich alle Ansätze - hin zu Clean Code und beeinflusst das. Aber das sind dennoch zwei Paar Schuhe.

Aber vielleicht kann ich mich auch nicht ganz klar ausdrücken oder habe irgendwas falsch im Kopf. Ich möchte nicht einmal ausschließen, dass ggf. Begriffe von mir leicht verfälscht verwendet werden und ich unter Design im Detail etwas Anderes verstehe als ihr. Nicht, dass es hier zu Wortklauberei kommt. Ich denke, wir sind nicht einmal weit auseinander mit den Sichtweisen...
 
mrBrown

mrBrown

Super-Moderator
Mitarbeiter
Also gewisse Dinge gehören etwas zusammen. Und auch die Ausführungen von @httpdigest sind durchaus korrekt. Aber dennoch sehe ich Design Entscheidungen von TDD abgekoppelt. Diese werden dann bei TDD explizit umgesetzt (wenn alle Tests erfolgreich sind), aber ich sehe es als einen eigenständigen Prozess an. Design muss man sich überlegen und das, ehe man anfängt, ersten Code zu schreiben. Was für Klassen willst Du denn schreiben, wenn Du Dir das noch nicht überlegt hast? Und der Punkt beim TDD ist eher:
TDD ist durchaus auch als Design-Tool gedacht, wenn man bereits alle nötigen Klassen vorher im Detail geplant hat, verbaut man sich damit relativ viel.

Was man natürlich haben sollte, ist ein Verständnis der Domäne – dabei fallen aber mindesten keine Methoden und auch keine Infrastruktur- und Applikation-Klassen bei raus. Am besten ist es wirklich, nur mit User-Storys & Domänenwissen zu starten, und alles weitere "entstehen zu lassen"

Hast du das Buch von Kent Beck mal gelesen? Auch ganz unabhängig von TDD ist das zu empfehle, grad die vielen Tipps sind relativ allgemein verwendbar ("Kauf dir einen vernünftigen Stuhl") :)
Das "Money"-Beispiel daraus ist ganz interessant, das startet wirklich bei Null – einziges "Vorwissen" ist das ungefähre Ziel, was möglich sein soll (würde man zB als User-Story formulieren), gestartet wird dann mit einem "2 mal 5 Dollar sind 10 Dollar", und am Ende fallen dann mehrere Klassen bei raus, keine von denen wurde vorher irgendwie eingeführt oder erwähnt, entstanden sind die rein durch Refactoring und Tests.
 
kneitzel

kneitzel

Top Contributor
Das Buch habe ich zumindest teilweise gelesen. Ich muss es wohl noch einmal hervor holen und durchgehen...

Habe jetzt auf jeden Fall erst einmal mitgenommen, dass ich die Thematik noch einmal vertiefen muss da ich ggf noch Lücken habe. Und schaden kann es bestimmt nicht ... war halt in den letzen Jahren etwas viel (durfte wieder C++ vertiefen, das hatte ich 2003 oder so zuletzt gemacht und da gab es doch einiges Neues und man vergisst manches auch bzw. hat es nichtmetrischer ganz so parat).

Danke auf jeden Fall für euren Input / eure Geduld.
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
N Fehler bei JUnit Test Java Basics - Anfänger-Themen 5
W JUnit Tests Java Basics - Anfänger-Themen 4
hello_autumn Klassen Anzahl sowie die Anzahl der Junit Tests ermitteln? Java Basics - Anfänger-Themen 8
B JUnit / Exceptions/ try-catch Java Basics - Anfänger-Themen 6
L JUnit tests in java Java Basics - Anfänger-Themen 5
N JUnit und private Methoden testen. Java Basics - Anfänger-Themen 9
F JUnit - Was ist mit "side effects" gemeint ? Java Basics - Anfänger-Themen 2
H JUnit in Eclipse: java.lang.NoClassDefFoundError: Java Basics - Anfänger-Themen 9
B JUnit Test erstellen Java Basics - Anfänger-Themen 6
W Problem bei JUnit Test Aufgabe Java Basics - Anfänger-Themen 15
L Junit Testing bei XML? Java Basics - Anfänger-Themen 3
J LocalDateTime testen mit Junit Java Basics - Anfänger-Themen 20
W JUnit Test und HashCode Java Basics - Anfänger-Themen 14
A Objekt in Methode zurückgeben, JUnit zeigt Error Java Basics - Anfänger-Themen 2
A Kfz - Händler Klasse. JUnit-Test gibt noch Fehler an, aber finde Ursache nicht Java Basics - Anfänger-Themen 7
O JUnit - Objektreferenzen Java Basics - Anfänger-Themen 3
G Testen mit JUnit Java Basics - Anfänger-Themen 4
B Palindrom Test mit Junit Java Basics - Anfänger-Themen 23
C JUnit Tests. How to Java Basics - Anfänger-Themen 5
S Junit Test Java Basics - Anfänger-Themen 2
shiroX Klassen Klasse/Methode private final jUnit-Fehler Java Basics - Anfänger-Themen 5
L Junit Tests Java Basics - Anfänger-Themen 10
A IllegalArgumentException in JUnit testen Java Basics - Anfänger-Themen 3
V ToString-Methode mit JUnit testen(BlueJ) Java Basics - Anfänger-Themen 10
shiroX Methoden JUnit-Test einer void-Methode Java Basics - Anfänger-Themen 4
V JUnit Klassen Java Basics - Anfänger-Themen 3
T Junit Mockito: Instanz von inneren erzeugten Objekten Java Basics - Anfänger-Themen 4
S JUnit - Swing- Anwendung wird nicht neu gestartet Java Basics - Anfänger-Themen 0
B Binäre Suche - Junit Test Java Basics - Anfänger-Themen 6
S Kommt es zu Seiteneffekten wenn man waehrend den laufenden JUnit Tests den Code aendert? Java Basics - Anfänger-Themen 2
M JUnit Testmethoden mit mehreren assert Methoden Java Basics - Anfänger-Themen 1
S Double und Gleitkommazahlen mit JUnit testen Java Basics - Anfänger-Themen 7
K JUnit: Objekte von eigenen Klassen vergleichen...geht nicht Java Basics - Anfänger-Themen 5
Z JUnit Exception Java Basics - Anfänger-Themen 2
M Verständnisfrage zu JUnit Tests und private Methoden Java Basics - Anfänger-Themen 3
M Ist die Hamcrest Bibliothek auch schon in Junit 4.11 verfügbar? Java Basics - Anfänger-Themen 1
S Unterschied .jar Datei ausführen und junit Testfall... Java Basics - Anfänger-Themen 3
M Reihenfolge von Testmethoden in JUnit beeinflussen Java Basics - Anfänger-Themen 2
S Separate Funktion für JUnit-Test Java Basics - Anfänger-Themen 3
G JUnit-Tests im Programmdurchlauf starten Java Basics - Anfänger-Themen 4
T Best Practice JUnit: Wie Klassen durch Stubs/Mockups ersetzen Java Basics - Anfänger-Themen 7
T JUnit test failed Java Basics - Anfänger-Themen 3
M Junit Tests durchführen, die eine Verbindung zu einer Daten erfordern Java Basics - Anfänger-Themen 3
T Junit in Eclipse Java Basics - Anfänger-Themen 1
P JUnit bedeutungen Java Basics - Anfänger-Themen 3
R JUnit Test mit einer Dateistruktur als Testparameter Java Basics - Anfänger-Themen 3
shiroX OOP Array kleinste Zahl mit jUnit test Java Basics - Anfänger-Themen 3
S JUnit assertEquals funktioniert nichgt wie es sollte :( Java Basics - Anfänger-Themen 7
V Frage zu JUnit Tests Java Basics - Anfänger-Themen 3
B JUnit für JFileChooser Java Basics - Anfänger-Themen 6
S Code stimmt nicht für vorgegebenen JUnit-Test Java Basics - Anfänger-Themen 2
S File vergleich - Junit Java Basics - Anfänger-Themen 6
T JUnit Java Basics - Anfänger-Themen 18
G Junit Java Basics - Anfänger-Themen 4
X JUnit testing Java Basics - Anfänger-Themen 7
T JUnit Suite frage Java Basics - Anfänger-Themen 6
R JUnit Test mit mehrfach ausgeführt Java Basics - Anfänger-Themen 6
S InvocationTargetException bei JUnit Testlauf Java Basics - Anfänger-Themen 2
B JUnit - Mini-Test Java Basics - Anfänger-Themen 9
T Unterschied zwischen Integrationstest und JUnit test? Java Basics - Anfänger-Themen 12
Y Junit Test - Testwert ändert sich Java Basics - Anfänger-Themen 12
T Junit --Exception testen Java Basics - Anfänger-Themen 15
A JUnit Tests in Jar-Archiv packen Java Basics - Anfänger-Themen 2
G Erste Schritte JUNIT Regressionstests automatisieren Java Basics - Anfänger-Themen 2
M JUnit - nur einzelne Methode testen? Java Basics - Anfänger-Themen 4
M JUnit - nur Failures loggen? Java Basics - Anfänger-Themen 2
S Hilfe zu Java-Programm und JUnit Test!! Java Basics - Anfänger-Themen 5
T JUNit Test IOException Java Basics - Anfänger-Themen 5
R String Replace für JUnit Java Basics - Anfänger-Themen 19
T JUNIT Nullpointerexception Java Basics - Anfänger-Themen 3
M JUNIT tests mit ant script ausführen Java Basics - Anfänger-Themen 4
S [JUnit] eigener Testsuite Runner + Ausgabe Java Basics - Anfänger-Themen 6
S [JUnit] Print Results while running Java Basics - Anfänger-Themen 6
W Mit jUnit Array testen? Java Basics - Anfänger-Themen 5
T Junit Tests Java Basics - Anfänger-Themen 7
I JUnit Datapoints für mehrere Testklassen Java Basics - Anfänger-Themen 4
Spin JUNIT Test Case - Problem bei testen Java Basics - Anfänger-Themen 2
T brauche HILFE beim Junit test:eek: Java Basics - Anfänger-Themen 11
M Junit tests gehen nicht Java Basics - Anfänger-Themen 2
E OOP einfache Array Aufgabe mit jUnit Java Basics - Anfänger-Themen 5
L JUNIT Testing Java Basics - Anfänger-Themen 3
C jUnit: Erfahren, ob der getestete Code eine Exception gehandelt hat Java Basics - Anfänger-Themen 3
timbeau JUnit Test Dauer speichern/loggen Java Basics - Anfänger-Themen 16
turmaline JUnit testen ob keine Exception auftritt Java Basics - Anfänger-Themen 23
D JUnit auf Matrix anwenden Java Basics - Anfänger-Themen 5
W JUnit 4.X und Parameter Java Basics - Anfänger-Themen 4
xehpuk In JUnit über eigenen Thread testen Java Basics - Anfänger-Themen 3
P JUnit - Optimierung der Testklassen Java Basics - Anfänger-Themen 13
S JUnit: Konsole aktualisiert sich nicht Java Basics - Anfänger-Themen 3
G JUnit ist das neue main() Java Basics - Anfänger-Themen 16
A junit test wann verwendet man "was"? Java Basics - Anfänger-Themen 4
J JUnit Test Java Basics - Anfänger-Themen 2
H JUnit Customizing Java Basics - Anfänger-Themen 5
S JUnit Java Basics - Anfänger-Themen 6
M Junit TestCase für Random() Zahlen Java Basics - Anfänger-Themen 6
R Junit - assertEquals Java Basics - Anfänger-Themen 4
N Junit-hilfe !!! Java Basics - Anfänger-Themen 4
F Gleiche JUnit-Tests automatisert mit verschiedenen Methoden ausführen Java Basics - Anfänger-Themen 6
Houly JUnit Test Suite anlegen Java Basics - Anfänger-Themen 6
B OOP Testen - JUnit Java Basics - Anfänger-Themen 30

Ähnliche Java Themen

Anzeige

Neue Themen


Oben