Wieso krieg ich die Unit Tests nicht hin

Y

yfons123

Gast
Ich habe einige Probleme beim Unit Tests und es läuft eigentlich immer in Quatsch aus was ich mache

1.
Bei TDD sollte ich einen Test schreiben und den Code anpassen dass der Test erfolgreich ist
das resultiert zb in so einen Code
Java:
public void isEven(int n){
    if(n == int.maxValue){
        return false;
    }
    try{
        isEven(n+2);
    }catch(OverFlowException ex){
        return true;
    }
}
der code ist zwar miserabel aber erfüllt die Unit Tests also denkt man sich "wunderbar das funktioniert also weiter gehts"
ich muss ja nicht nachdenken, wenn die tests funktionieren passt ja alles.

dann komm ich zum nächsten problem

2.
wenn man zu viele UnitTests hat ist es fast unmöglich irgendwas an den Anforderungen zu ändern da man ansonsten den halben Tag rein steckt die Unit Tests so anzupassen dass sie den neuen anforderungen entsprechen

ich verballere mehr zeit damit die Unit Tests an neue anforderungen anzupassen als ich damit verbringe unit tests neu zu schreiben oder die methode zu schreiben

3.
die "verwaltung" der unit tests
wie zb Testdateien erzeugen , doku , kommentare, ANFORDERUNGS Anpassungen fressen einfach zu viel zeit


irgendwas mache ich so dermaßen falsch dass es gerade so rauscht...
 

KonradN

Super-Moderator
Mitarbeiter
Sorry, im Augenblick stehe ich nur da und frage mich: Was für Tests wurden da geschrieben, dass da so ein Quatsch an Code raus gekommen ist? Zumal der Code nicht übersetzt mit diesem int.maxValue, aber ok, da meinstest Du ein Integer.MAX_VALUE. Aber darum soll es nicht gehen. Wie kommt man zu so einem Code? Das schafft ja noch nicht mal unser Mr. Ich mache mir ständig neue Accounts.

Bei TDD ist es existenziell, dass kleine Schritte gegangen werden. Dann ein Test der fehlschlägt und dann eine minimale Anpassungen, damit der Test nicht mehr fehlschlägt. Und dann kommt ein Refactoring.

wenn man zu viele UnitTests hat ist es fast unmöglich irgendwas an den Anforderungen zu ändern da man ansonsten den halben Tag rein steckt die Unit Tests so anzupassen dass sie den neuen anforderungen entsprechen
Wenn der Code sauber unterteilt wurde und die Aufgaben sauber deligiert wurden und bei Unit Tests nur die eigentliche Methode getestet wird und nicht mehr, dann passiert sowas nur, wenn die Anforderungen entsprechend großflächig geändert wurden. Ansonsten hast Du eben lokale Anpassungen und das sollte dann auch nur bei den entsprechenden Tests aufschlagen.
Hier ist also speziell die Single Responsibility Principle zu nennen sowie Mocking!

die "verwaltung" der unit tests
wie zb Testdateien erzeugen , doku , kommentare, ANFORDERUNGS Anpassungen fressen einfach zu viel zeit
Da wäre die Frage, was genau Du da treibst. Meine Erfahrung ist, dass Unit Tests durchaus Zeit kosten, aber diese Zeit wird später um ein vielfaches herein geholt. Alleine schon, was es kostet, wenn beim Kunden Probleme auftreten und man diese erst einmal rekonstruieren muss.

Aber bei Dir läuft ganz offensichtlich irgendwas gewaltig schief. Und das kann dann tatsächlich erklären, dass Du da natürlich auch in Zeitprobleme kommen wirst. (Wobei das nichts mit den Unit Tests zu tun hat. Ungetesteten Code von Dir möchte ich - bei dem Beispielcode - gar nicht erst sehen!)
 

KonradN

Super-Moderator
Mitarbeiter
Ansonsten:
a) Buch: Kent Beck: "Test Driven Development by example"
b) Einführungen in TDD: https://www.java-forum.org/thema/test-driven-development-anhand-von-fizzbuzz.189510/ und https://www.java-forum.org/thema/tdd-beispiel-2-kuerzeste-pfade-in-graphen.189527/

Bei den Threads von @mihe7 gefällt mir nicht, dass da die Anforderungen nicht festgelegt sind. Das ist aus meiner Sicht ein Unding und auch unrealistisch. Aber für Dich evtl. gut, denn da müssen dann tatsächlich die unittests angepasst werden (Weil z.B. die Rückgabe sich ändert und statt List<Integer> dann plötzlich List<String> zurückgegeben wird).
 
Y

yfons123

Gast
Sorry, im Augenblick stehe ich nur da und frage mich: Was für Tests wurden da geschrieben, dass da so ein Quatsch an Code raus gekommen ist? Zumal der Code nicht übersetzt mit diesem int.maxValue, aber ok, da meinstest Du ein Integer.MAX_VALUE. Aber darum soll es nicht gehen. Wie kommt man zu so einem Code? Das schafft ja noch nicht mal unser Mr. Ich mache mir ständig neue Accounts.
es war als beispiel gedacht, ich drifte bei tests sehr schnell ins lächerliche ab
 

KonradN

Super-Moderator
Mitarbeiter
Also der Code war erst einmal kein Test sondern wohl ein Code, den Du dann getestet hast.

Bau doch mal irgend ein reelles Beispiel, das sowohgl Tests also auch die daraus resultierenden Implementationen zeigt. Dann kann man da in der Regel am ehesten helfen.

Test Driven bedeutet ansonsten ja in erster Linie, dass man erst den Test schreibt. Das muss aber nicht in so vielen kleinen Schritten passieren, wie es z.B. bei den Threads von @mrBrown gezeigt wurde. Wobei das etwas Erfahrung ist, um zu sehen, was Sin macht und was kein Sinn macht. Die Implementierungen müssen trivial bleiben.

Beispiel: Es wird ein isEval gebraucht. Dann überlege ich schon vorab, wie das denn zusammen gehört - und dann ist das von mir aus eine Math Utility Klasse. Und dann überlege ich ich mir dieses Thema direkt. Und dann ist da direkt ein Test wie:

Java:
    @ParameterizedTest(name = "Is {0} eval? -> {1}")
    @CsvSource({
            "0, true",
            "1, false",
            "2, true",
            "-1, false"
    })
    public void testEval(int value, boolean expectedResult) {
        assertEquals(expectedResult, MathUtil.isEven(value));
    }

Und dann sieht die Implementation direkt so aus:
Java:
public class MathUtil {
    public static boolean isEven(final int value) {
        return value % 2 == 0;
    }
}

Edit: Bei dem Code habe ich auf die JavaDoc Kommentare verzichtet. Die sind aber angebracht, da diese beim Unit Test beschreiben, was die Spezifikation ist (Das finde ich einen super Ort!) und man will den eigenen Code ja dokumentieren (JavaDoc generieren und fertig!)
Die Schrittweite ist aber halt so gewählt, dass da wirklich nur minimaler Code geschrieben werden muss. Da kommt also nur eine kleine Anpassung oder eine kleine Methode bei raus. Da entstehen keine zig Methoden, mehrere Klassen und und und ....

Wenn ich feststelle: Ich muss da diverse Fälle beachten (Mehrere if Statements), dann stoppe ich, aus dem Test werden direkt 2 oder mehr Tests (Von denen ich dann aber nur einen beachte!) Man arbeitet daran so, wie der Vater die Klöße isst: Einen nach dem anderen!


Was also für mich immer dazu gehört ist die Planungsphase. Ohne geht es doch auch gar nicht. TDD setzt doch eigentlich voraus, dass es eine gewisse Planung gibt. In der Regel ist dies ein Agiler Ansatz:
  • Man hat eine Art Backlog. Da sind dann Features / User Stories / ... gesammelt.
  • Davon hat man eine gewisse Menge für eine Iteration vorgesehen. Dazu wurden die aber bereits eingeordnet nach Aufwand, Abhängigkeiten u.s.w.
  • Wenn man die "gezogenen" Einträge hat, dann werden Tasks geplant. Du hast also einen Task zu implementieren, der nur so bis 4h dauern sollte (Absolutes Maximum 2 Tage!). Also vom Aufwand her überschaubar.

Das hat man aber doch nur, wenn man ein Gesamtbild hat. Nur wenn ich ein Gesamtbild habe, weiss ich, was ich brauche.

Damit habe ich ein überschaubaren Task, den ich bearbeiten muss. Dabei stelle ich dann fest: Ich muss irgendwo abprüfen, ob eine Zahl gerade ist. Also mache ich diese Methode. Das mache ich aber doch nicht irgendwie ("Ach, ich soll da war mit einem Auto machen - packe ich das also einfach in die Klasse "Car". So geht doch hoffentlich niemand vor, denn SOLID gilt ja bei jeder Entwicklung.) Wobei das sogar auch ginge. Aber nach dem erfolgreichen Test (nach der "rot" und "grün" Phase) würde dann das Refactoring kommen - damit würde dann spätestens auf die SOLID Principles geachtet.

Edit: mrBrown und nicht mihe7 - sorry für die Verwechslung und Danke für den Hinweis.
 
Zuletzt bearbeitet:

KonradN

Super-Moderator
Mitarbeiter
Die TDD-Threads waren von @mrBrown :)
Ach je, irgendwie werfe ich euch beide immer durcheinander ... aber schlimm ist ja, dass ich die Links erst geöffnet hatte - da hätte ich es sehen können / müssen.

Sorry und schande Asche auf mein Haupt.

Edit: Habs in meinem Beitrag noch korrigieren können. Und habe hier noch Rechtschreibung angepasst. Und die Redewendung ist "Asche auf mein Haupt" meine ich, oder?
 

LimDul

Top Contributor
der code ist zwar miserabel aber erfüllt die Unit Tests also denkt man sich "wunderbar das funktioniert also weiter gehts"
ich muss ja nicht nachdenken, wenn die tests funktionieren passt ja alles.
Das ist ein elementarer Fehler. TDD besteht aus drei Schritten:
a) Schreibe roten Testfall
b) Schreibe Code der den Testfall grün macht
c) Refaktore den Code, bis er ordentlich ist ohne die Tests kaputt zu machen.

Und der dritte Schritt ist der essentielle Schritt, ohne den kommt Murks raus. Die beiden anderen Schritte stellen nur sicher, dass man auf dem Weg zu ordentlichen Code Netze & doppelten Boden hat.
 

thecain

Top Contributor
2.
wenn man zu viele UnitTests hat ist es fast unmöglich irgendwas an den Anforderungen zu ändern da man ansonsten den halben Tag rein steckt die Unit Tests so anzupassen dass sie den neuen anforderungen entsprechen
Noch kurz zu dem Punkt: Beim Punkt Refactoring ist es auch erlaubt Test zu refactoren oder falls doppelt zu entfernen.
 
Y

yfons123

Gast
Und nicht entmutigen lassen! TDD muss man lernen und üben, alleine das braucht auch Zeit.
ich habe bis jetzt 1en sinnvollen test geschrieben

ich hab was geändert und plötzlich hat die methode null zurück gegeben und ohne den Test hätte ich es nicht gefunden... hätte irgendwo in git commits rum schaufeln müssen und suchen "wie wars denn vorher"
 

LimDul

Top Contributor
Tests dienen zu deutlich mehr Dingen, als nur sicherstellen "Bei Änderung macht niemand was kaputt". Auch wenn ich persönlich mit TDD nicht warm werde, ist der Gedanke dahinter sinnvoll.

Tests dienen dazu:

Sicherzustellen das bei Änderungen nichts kaputt geht
Das ist der offensichtlichste Punkt und der, auf dem am Anfang der meiste Fokus liegt

Sicherstellen, das man an alles in der Implementierung gedacht hat
Unabhängig ob man mit TDD arbeitet oder Tests danach schreibt. Die Überlegung "Was sind erlaubte Eingaben und was erwarte ich eigentlich als Ergebnis" ist eine Sichtweise, die extrem wichtig ist. Mal ganz banales Beispiel, Quicksort für ein Array zu schreiben ist nicht schwer. Aber wenn man die Tests schreibt, sollte man auch mal testen:
  • Was ist, mit einem Array der Länge 0?
  • Was ist, wenn das Array null ist?
  • Was ist, wenn das Array null-Elemente enthält?

Vor allen Dingen die Randfälle (wie Länge 0) oder Sonderfälle (null) übersieht man gerne bei der Implementierung. Ich kann die Fälle nicht mehr zählen, wo die Tests mir Flaws in meiner Umsetzung aufgezeigt haben. Und das deutlich schneller, als die Anwendung zu bauen, auf einen Server zu deployen und die Konstellationen von Hand durchzuklicken.

Eine saubere, vernünftige API sicherstellen
Das sehe ich als größten Benefit von TDD. Man stellt sicher, dass der Code auch wirklich getestet wird. Schreibt man erst Code und dann Tests stellt man fest, dass man gewisse Stellen mit Tests nicht rankommt, weil das so tief in private Methoden ist, dass es nicht testbar ist. Das heißt, die API ist klar und ich kann alles gewünschte Verhalten über die API meiner "Unit" ansteuern. Und es gibt kein internen Verhalten, was nicht ansteuerbar ist

Dokumentation
Javadoc veraltet, wird nicht gepflegt. Weil niemand stellt fest, wenn es irgendwann mal falsch ist. Mit sauberen Unit-Tests hat man gleich eine Dokumentation einer Schnittstelle. Schaut man sich mal z.B. die Dokumentation von https://commons.apache.org/proper/c...StringUtils.html#center-java.lang.String-int- an, stellt man fest, im Prinzip besteht die Hälfte des Javadocs eigentlich aus Unit Tests:
StringUtils.center(null, *) = null
StringUtils.center("", 4) = " "
StringUtils.center("ab", -1) = "ab"
StringUtils.center("ab", 4) = " ab "
StringUtils.center("abcd", 2) = "abcd"
StringUtils.center("a", 4) = " a "
 
Y

yfons123

Gast
mir wurde gesagt dass ich für "interne" annahmen einfach asserts in den code selber werfen sollte
dass unit tests nur von außen testen was raus kommt

und man sollte intern auch asserts und tests schreiben
 

LimDul

Top Contributor
Intern machst du assert um sicherzustellen, dass die Konstellation ok ist.

Ein klassisches Problem ist oft z.B. LocalData.now()

Java:
public MyPerfectClass() {
public int berechneAnzahlTag() {
LocalDate datum = LocalDate.now();
// Ganz viel Code der aus datum was berechnet
}

Funktioniert der Code am 29.2? Am 31.12? Am 1.1?

So nicht testbar, weil du diese Konstellation nicht ansteuern kann. Und das am 1.1/31.12 oder 29.2 Code kaputt geht ist ein Klassiker.

Schreibt man TDD, sorgt man automatisch dafür, dass das Datum für Tests beeinflussbar ist. Und wenn es nur sowas ist:

Java:
public MyPerfectClassTestbar() {

protected LocalDate getCurrentDate() {
return LocalDate.now();
}
public int berechneAnzahlTag() {
LocalDate datum = getCurrenDate();
// Ganz viel Code der aus datum was berechnet
}

Dann kann ich zumindest eine Testklasse als Ableitung anlegen und dort getCurrentDate überschreiben und damit im Test verschiedene Konstellationen simulieren. (Sauberer wäre es mit Clock zu arbeiten und die Clock von außen zu beeinflussen: https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/time/Clock.html)
 
Y

yfons123

Gast
Java:
protected LocalDate getCurrentDate()
das ist protected also "unerreichbar" ohne reflection
 
Y

yfons123

Gast
nö.. hab noch nichts mit mocks gemacht

da ich in die Admin schiene gegangen bin hab ich nur durch "persönliche" Projekte sachen mitgekriegt für normale software entwicklung dementsprechend bin ich blank aufgestellt wenns tiefer rein geht
 

LimDul

Top Contributor
Java:
protected LocalDate getCurrentDate()
das ist protected also "unerreichbar" ohne reflection
Der Test sehe (grob) so aus:

Java:
public void MyTestClass() {

  private LocalDate testDatum;
  private MyPerfectClassTestbar classUnderTest = new TestMyPerfectClassTestbar();

  @Test
  public void testDoSomething() {
    testDatum = LocalDate.of(2020, 2, 29)
    classUnderTest.doSomething();
    assertThat(....);
  }

  private class TestMyPerfectClassTestbar extends MyPerfectClassTestbar {
    protected LocalDate getCurrentDate() {
      return testDatum;
    }
  }
}
 
Y

yfons123

Gast
Der Test sehe (grob) so aus:

Java:
public void MyTestClass() {

  private LocalDate testDatum;
  private MyPerfectClassTestbar classUnderTest = new TestMyPerfectClassTestbar();

  @Test
  public void testDoSomething() {
    testDatum = LocalDate.of(2020, 2, 29)
    classUnderTest.doSomething();
    assertThat(....);
  }

  private class TestMyPerfectClassTestbar extends MyPerfectClassTestbar {
    protected LocalDate getCurrentDate() {
      return testDatum;
    }
  }
}
das hat ja dann zur schlussfolge dass das date now gar nicht in betracht gezogen wird was ursprünglich da war
 

M.L.

Top Contributor
Vorausgesetzt, es ist wirklich C#-Code, könnte man im passenderen Ökosystem recherchieren: mycsharp.de -> Artikel -> [Artikel] Unit-Tests: Einführung in das Unit-Testing mit VisualStudio (weiteres generelles Stichwort auch: parametrische Tests)
 

KonradN

Super-Moderator
Mitarbeiter
Einen ganz wichtigen Punkt, der schon etwas angeklungen ist, möchte ich noch vertiefen. Der Fokus, was getestet werden soll!

Man will genau einen spezifischen Punkt der Spezifikation testen. Dann bedeutet dies aber auch: Wirklich nur genau diesen einen spezifischen Punkt!

Das ist ganzwichtig, denn dadurch wird erreicht, dass eben Änderungen sich nur lokal auswirken.

Ich konstruiere ein kleines Beispiel:
- Wir haben einen FizzBuzzValue Generator. Der bekommt also eine Zahl als Parameter und gibt einen String zurück. Von der Implemenmtation her etwas wie (Auszug):
Java:
    public String generate(final int number) {
        if (number < GENERATE_MIN_VALUE) {
            throw new IllegalArgumentException("Number must be at least " + GENERATE_MIN_VALUE);
        }

        if (isFizz(number) && isBuzz(number)) {
            return FIZZBUZZ_VALUE;
        }
        if (isFizz(number)) {
            return FIZZ_VALUE;
        }
        if (isBuzz(number)) {
            return BUZZ_VALUE;
        }
        return String.valueOf(number);
    }

- Nun haben wir noch eine Klasse, die eine Liste von FizzBuzzWerten erzeugt:
Java:
public class FizzBuzzListGenerator {
    private FizzBuzzGenerator generator = new FizzBuzzGenerator();
  
    public List<String> generateList(int upTo) {
        return IntStream.rangeClosed(1, upTo)
                .mapToObj(generator::generate)
                .toList();
    }
}

Und diese Klasse / Methode wollen wir nun testen.

Prinzipiell ist es einfach zu testen:
Java:
class FizzBuzzListGeneratorTest {

    @ParameterizedTest
    @CsvSource(value = {
            "0",
            "1, 1",
            "2, 1,2",
            "3, 1,2,Fizz"
    })
    public void testGenerateList(int upTo, @AggregateWith(ListAggregator.class) List<String> result) {
        FizzBuzzListGenerator generator = new FizzBuzzListGenerator();
        assertEquals(result, generator.generateList(upTo));
    }

    static class ListAggregator implements ArgumentsAggregator {

        @Override
        public Object aggregateArguments(ArgumentsAccessor arguments, ParameterContext context) {
            List<String> result = new ArrayList<>();

            for (int i = 1; i < arguments.size(); i++) {
                result.add(arguments.getString(i));
            }

            return result;
        }
    }
}

Das beschriebene Problem ist jetzt aber: Wir testen nicht nur den FizzBuzzListGenerator.generateList() sondern wir testen auch den FizzBuzzGenerator.generate. Wenn sich der FizzBuzzGenerator ändert, dann brechen wir nicht nur den Unit Test vom FizzBuzzGenerator (was korrekt wäre - die Änderung wäre ja eine Änderung der Spezifikation. Bei durch 4 teilbaren Werten kommt noch ein "Stop" dazu. 4 -> "Stop", 12 -> "FizzStop", 20 -> "BuzzStopp", 60 -> "FizzBuzzStop" ....)

Damit haben wir hier das Problem:
wenn man zu viele UnitTests hat ist es fast unmöglich irgendwas an den Anforderungen zu ändern da man ansonsten den halben Tag rein steckt die Unit Tests so anzupassen dass sie den neuen anforderungen entsprechen

Also müssen wir das verändern. Wir müssen die Abhängigkeit raus nehmen. Und das geht ganz simpel:
1. Wir erweitern die Klasse etwas:
Java:
    private final FizzBuzzGenerator generator;

    public FizzBuzzListGenerator() {
        generator = new FizzBuzzGenerator();
    }

    public FizzBuzzListGenerator(FizzBuzzGenerator generator) {
        this.generator = generator;
    }
Damit haben wir zwei Konstruktoren - einen, der so wie der bisherige Konstruktor ein Standard Objekt braucht. Und einen, der der halt den Generator entgegen nimmt.

2. Nun können wir beim Unit Test einen neuen Generator unterschieben. Und da ist es am einfachsten, dass wir einfach die Werte 1:1 durchreichen:

Java:
    @ParameterizedTest
    @CsvSource(value = {
            "0",
            "1, 1",
            "2, 1,2",
            "3, 1,2,3"
    })
    public void testGenerateList(int upTo, @AggregateWith(ListAggregator.class) List<String> result) {
        FizzBuzzGenerator fizzBuzzGenerator = Mockito.mock(FizzBuzzGenerator.class);
        lenient().when(fizzBuzzGenerator.generate(anyInt())).thenAnswer( i -> i.getArgument(0).toString() );

        FizzBuzzListGenerator generator = new FizzBuzzListGenerator(fizzBuzzGenerator);
        assertEquals(result, generator.generateList(upTo));
    }

Wir mocken also den FizzBuzzGenerator. Und wenn generate mit irgend einem int aufgerufen wird, dann antworten wir: Vom Aufruf (i ist der invocation Parameter von der Answer.answer Methode) den ersten Parameter geben wir als String zurück.

Das lenient() am Anfang wird hier nur benötigt, da beim erstenb Testfall der Generator nicht aufgerufen wird. da wirft Mockito dann eine Exception, denn unnötige Vorgaben mag Mockito per default nicht. Und mit dem lenient() davor ist es dem Mockito bei der Regel egal.

Und nun ist uner Ergebnis: Den FizzBuzzGenerator können wir so viel anpassen, wie wir wollen. Der Unit Test vom FizzBuzzListGenerator wird davon nicht beeinflusst.

Refactorings, die denkbar sind: Man kann das alles noch über ein Interface machen. Dem List Generator ist der ValueGenerator ja egal - daher kann man da ein funktionales Interface bauen und es entkoppeln. Und das ist etwas, das einem doch schon fast in den FIngern juckte, wenn man den Code da so gesehen hat.

Edit: kleine Ausbesserung im Wording
 

LimDul

Top Contributor
Refactorings, die denkbar sind: Man kann das alles noch über ein Interface machen. Dem List Generator ist der ValueGenerator ja egal - daher kann man da ein funktionales Interface bauen und es entkoppeln. Und das ist etwas, das einem doch schon fast in den FIngern juckte, wenn man den Code da so gesehen hat.
Das ist übrigens der Punkt, den ich als größten Benefit von TDD sehe (auch wenn ich selber damit wie gesagt nicht warm werde).

Man will in Regel kleine Funktionalitäten testen. Die kleinste, testbare Einheit ist eine Methode. => Man schreibt nur kleine Methoden und splittet das Problem auf.

Man will spezifische Funktionalität testen und unabhängig von andere Komponenten testen. => Man sorgt dafür, dass die anderen Komponenten nicht im Bauch irgendwo erzeugt werden, sondern sauber und klar an einer Stelle definiert werden. Am besten auch von außen reingegeben. Hier kommt dann auch die Dependency Injection zum tragen, die das unterstützt. Man hat dann also eine Komponenten Architektur die transparent ihre Abhängigkeiten macht.


Man will seine eigene Funktionalität testen und nicht andere Komponenten mit testen. => Man tauscht andere Komponenten durch Mock/Dummy-Implementierungen aus. Um sicherzustellen, dass man immer noch das richtige testet, wird es essentiell, dass die Schnittstelle dieser Komponente klar definiert ist. Ansonsten weiß man nicht, ob die Mock/Dummy-Implementierung was sinnvolles tut. Das wiederum sorgt dafür, dass man ggf. Interfaces anlegt und das die APIs von anderen Komponenten klar und verständlich sind.


Das ganze macht das Testen aber auch alles andere als trivial und der Sprung von "Ich mache einen Unit-Test meiner add(int a, int b) Methode" hinzu "Ich verbessere und sichere meine Architektur mit Unit-Tests" ist extrem groß. Deswegen auch nicht aufgeben - man lernt immer was dazu.

Auch ich hab trotz zig Jahren Software-Entwicklung durch diesen Thread noch mal den ein oder anderen Blickwinkel bekommen, der mir vorher so nicht präsent war. Man lernt halt nie aus :)
 
Y

yfons123

Gast
Wir mocken also den FizzBuzzGenerator. Und wenn generate mit irgend einem int aufgerufen wird, dann antworten wir: Vom Aufruf (i ist der invocation Parameter von der Answer.answer Methode) den ersten Parameter geben wir als String zurück.

Das lenient() am Anfang wird hier nur benötigt, da beim erstenb Testfall der Generator nicht aufgerufen wird. da wirft Mockito dann eine Exception, denn unnötige Vorgaben mag Mockito per default nicht. Und mit dem lenient() davor ist es dem Mockito bei der Regel egal.

Und nun ist uner Ergebnis: Den FizzBuzzGenerator können wir so viel anpassen, wie wir wollen. Der Unit Test vom FizzBuzzListGenerator wird davon nicht beeinflusst.
also verstehe ich das richtig dass

der mock versucht es unwichtig zu machen wie klasse X implementiert ist wenn Y ein objekt davon braucht
ohne mock würde ich ja bei der X änderung auch Y zerstören und mit dem mock umgehe ich das dann?
 
Y

yfons123

Gast
das ist jetzt c#

ich habe mal gesucht nach beispielen die ich in der vergangenheit hatte wo ich probleme hatte zum bleistift hier.
was hätte man anders machen können, sieht nicht richtig oder hilfreich aus

im prinzip habe ich eine mathe formel für meine Widerstands klasse
Code:
float Divider;
float clampedReductionMultiplier;
public float Calculate(float armor)
    {
        var divider = Math.Clamp(Divider, 0f, 100f);
        var clampedReductionMultiplier = Math.Clamp(ReductionMultiplier,0f,1f);
        return Math.Clamp((armor / (armor + divider))  * clampedReductionMultiplier ,0.1f,0.9f);
    }

C#:
    [Test]
    public void Calculate_ReturnsExpectedResult()
    {
        // Arrange
        var resistance = new Resistance() { Divider = 50f, ReductionMultiplier = 0.8f };

        // Act
        var result = resistance.Calculate(100f);

        // Assert
        Assert.AreEqual(0.53333f, result, 0.0001f);
    }
    [Test]
    public void MaximumValue()
    {
        // Arrange
        var resistance = new Resistance() { Divider = 0f, ReductionMultiplier = 1f };

        // Act
        var result = resistance.Calculate(100f);

        // Assert
        Assert.AreEqual(0.9f, result, 0.0001f);
    }
    [Test]
    public void MinimumValue()
    {
        // Arrange
        var resistance = new Resistance() { Divider = 50f, ReductionMultiplier = 0f };

        // Act
        var result = resistance.Calculate(100f);

        // Assert
        Assert.AreEqual(0.1f, result, 0.0001f);
    }
 

AndiE

Top Contributor
Ich finde, dass das kein gutes Beispiel ist.
<
Wenn ich eine Gleichung x^2+px+q auf Nullstellen berechne, dann kann ich drei Lösungsfälle haben:
keine Lösung(D<0)
eine Lösung(D=0)
zwei Lösungen(D>0)

mit D=(p^2)/4-q

Offensichtlich sollte ene Testsuite diese drei Fälle absichern sowie einige Fälle, bei denen x1 und x2 bekannt ist, und die bere3chneten mit den erwarteten Werten verglichen werden.
 
Y

yfons123

Gast
und da wirds dann wieder schwer sinnvolle sachen zu machen

das fühlt sich schon während dem bauen nutzlos an, in c# hab ich ncrunch was mir wenigstens halbwegs garantiert dass ich nichts zerschieße
was aber nicht hilft wenn meine tests von vorne herein kaum was abdecken
 

LimDul

Top Contributor
Prinzipiell fehlen da ein Haufen Testfälle.

Code:
var divider = Math.Clamp(Divider, 0f, 100f);
Wo ist der Test mit Divider < 0 oder > 100?

Code:
  var clampedReductionMultiplier = Math.Clamp(ReductionMultiplier,0f,1f);
Wo ist der Aufruf mit ReductionMultiplier <0 oder > 1?

Code:
        return Math.Clamp((armor / (armor + divider))  * clampedReductionMultiplier ,0.1f,0.9f);
Wo ist der Aufruf mit armor < 0 und armor+divider = 0?

Das sind die interessanten Testfälle.
 

KonradN

Super-Moderator
Mitarbeiter
Hast Du mal ein TDD Buch durchgearbeitet? Kent Becks "Test Driven Development by example" geht wirklich an Beispielen alles durch. Da bekommst Du dann mit, wie der Code aussieht und auch die Tests. Selbst wenn man nicht Test Driven arbeitet, schreibt man Code, der testbar ist.

Aber das ändert nichts daran, dass man lesbaren Code schreiben soll. Dein Code aus #35 ist absolut unleserlich. Was soll der Code sinnvolles machen? Das ist so nicht verständlich.

  • float Divider;
  • float clampedReductionMultiplier;
Du hast da also irgend einen State, der irgendwas aussagt.


`public float Calculate(float armor)
Super - es wird was berechnet. Das erklärt die Mathematische Formel natürlich direkt.


var divider = Math.Clamp(Divider, 0f, 100f); Also eine lokale Variable die genau so heißt wie der State der Instanz - nur mit kleinem Buchstaben. Und die magische Grenzen 0 - 100. Wieso gelten die bei divider und nicht bei Divider?

var clampedReductionMultiplier = Math.Clamp(ReductionMultiplier,0f,1f); Hier ist wenigstens ein klarer deutlicher Name. Aber warum Grenz von 0 - 1?

Diese zwei Zeilen haben irgend eine Business Logik, die nirgends erläutert wird. Was genau machst Du da? Das wären dann ggf. Methoden mit sinnvollem Namen.

return Math.Clamp((armor / (armor + divider)) * clampedReductionMultiplier ,0.1f,0.9f); Eine Berechnung - super. und wieder eine Begrenzung auf in Intervall ...



Und da sind dann einfache Regeln umzusetzen:
  • Keine Magic Numbers! Im code hat es keine 0f, 1f u.s.w. zu geben!
  • Vernünftige Bezeichner
  • Es wird nur eine Sache gemacht. Hier sind mehrere Dinge mit den ganzen Begrenzungen der Eingaben (das nenne ich mal so, auch wenn es ja ein State der Instanz ist).

Und jetzt ist mir @LimDul zuvor gekommen mit seiner Antwort, aber bei den Unit Tests musst Du anschauen, was es denn für Fälle gibt. Da sind dann die Grenzfälle wichtig. Also tatsächlich Werte, die Außerhalb der Grenzen liegen und angepasst werden (z.B.).

Und dann ganz wichtig: Dokumentiere den Code! Methoden und so werden dokumentiert! Das ist wichtig, denn Visual Studio zeigt Dir das dann auch an. Und es hilft, das dann auch zu verstehen, was Du da überhaupt machst.

Woher kommt diese Formel, nach der berechnet wird? Was ist das Ziel davon? Was die Anforderungen dahinter?
 

KonradN

Super-Moderator
Mitarbeiter
Und ein weiterer Gedanke kommt mir gerade: Kann es sein, dass Du einfach zu viel durcheinander machst? Du bis in PowerShell unterwegs, C#, Java, ... Du machst vieles etwas, aber vermutlich nichts richtig. Konzentriere Dich auf eines und meistere das! Dann hast Du eine Baustelle und kannst diese nach und nach immer weiter ausbauen. Kannst da dann immer mehr Dinge dazu nehmen.

Aber das ist ein Eindruck, der evtl. auch täuschen kann. Aber ich bin da relativ strikt: Ich mache eine Sache. Wenn ich diese eine Sache mache, dann bleibt alles Andere liegen soweit es geht. Die Themen sind einfach zu komplex!

Bei mir wäre das konkrete Beispiel: .Net MAUI mit Blazor ist etwas, das ich gerne machen würde um da rein zu kommen. Aber es gab auch eine Entscheidung zu Quarkus statt Spring Boot und dann ist das halt jetzt das Thema. Da bleibt Visual Studio halt jetzt weg und Quarkus ist das Thema. Und die Komplexität ist direkt da, denn da kommen dann ja auch viele Themen mit wie Keycloak, Docker, ....
 
Y

yfons123

Gast
die anforderungen sind:
Java:
es sollte die auswirkung der rüstung darstellen, als widerstand
    1. es muss ein prozentualer wert sein der den schaden verringert
    2. umso mehr rüstung man hat umso schlechter skaliert es
    3. man muss mindestens 10% schaden bekommen: schaden * ( 1 - widerstand) > schaden * 0.1
    4. man darf nicht den schaden um 0% verringern ( mögliche 0 division die nur ärger macht )
das war so der plan fürs erste.
dann bin ich nach geogebra gegangen und habe die formel aufgestellt, hier mal mit * 100 dass man es besser sieht was passiert

für die die es ausprobieren wollen:
d = 50
u = 1
f(x) = ((x)/(x+d)) u*100

x wäre hier die rüstung und Y der prozentuale reduzierer

1674855907832.png

( die klasse "Resistance" für die Werte wurden aus einer xsd generiert was die properties angelegt hat )
dann hab ich irgendwelche punkte der kurve genommen und diese in den hier vorliegenden test geschrieben.

und dann die formel in C# eingebaut, das war das vorgehen soweit

@LimDul
ich hätte es nicht hingekriegt die grenzfälle zu betrachten, ich tu mich da eigentlich ziemlich schwer
bei einer Mathe formel wäre es da eigentlich das einfachste wenn man das jetzt mit deinen beispielen betrachtet.. und nicht mal da krieg ich es hin

wie denkt man dass man die grenzfälle raus bekommt? sollte ich mir den dämlichst möglichen fall überlegen?

Kann es sein, dass Du einfach zu viel durcheinander machst? Du bis in PowerShell unterwegs, C#, Java, ... Du machst vieles etwas, aber vermutlich nichts richtig. Konzentriere Dich auf eines und meistere das!
ich muss in der Arbeit mit powershell und bash hantieren, da das die "als standard" gesetzten sprachen sind.
C# und java darf ich nicht hernehmen weil es kein anderer aus dem Team lesen kann. Damit bin ich da an Powershell gebunden.

Im privaten Bereich mache ich primär mit Godot C# aber für die "häuslichen anwendungen" nehme ich javafx her da ich das am besten kann von allen GUI Bibliotheken, ich sollte da wahrschienlich doch eher auf MAUI ( wo ich noch nichts gemacht habe ) umsteigen dass ich den java teil weg habe

Es wird nur eine Sache gemacht. Hier sind mehrere Dinge mit den ganzen Begrenzungen der Eingaben (das nenne ich mal so, auch wenn es ja ein State der Instanz ist).
ich hätte diese werte wahrscheinlich schon bei dem setzen überprüfen sollen da sie sowieso nicht mehr änderbar sind im nachhinein
 
Y

yfons123

Gast
Das ist doch ein super Beispiel: d, u, x. Y …..

Wie willst du da selbst durchschauen, wenn du da in paar Wochen noch einmal drauf schaust?
ich muss gestehen ich hab nicht erwartet dass geogebra es verträgt mehrere buchstaben zu benutzen.

ich wüsste ja nicht mal wie man es beschreiben sollte...
1674857917375.png
kurvigkeit => wie schnell kommt man auf den maximal wert der kurve
faktorerhöhung => "schiebt" die kurve in Y richtung, also reduziert den maximal wert der kurve

ich weis nicht mal wie man diese kurve überhaupt nennt oder wie man faktoren mathematisch beschreibt was die tun


PS:
ich muss auch noch die schadens berechnung machen und da versuche ich dann das hier anzuwenden.. mal schauen wie das hinhaut
 
Zuletzt bearbeitet von einem Moderator:
Y

yfons123

Gast
ich habe noch etwas an den Anforderungen geschraubt dass eine Obergrenze eigentlich komplett egal ist, nur die Prozentuale rückgabe muss im Bereich liegen

Hier mal das ganze so wie es jetzt rum steht.. ich habe versucht die Tests zu machen die Limdul gesagt hat und auch versucht den Code "lesbarer" zu machen nachdem was Konrad gesagt hat

C#:
//------------------------------------------------------------------------------
// <auto-generated>
// </auto-generated>
//------------------------------------------------------------------------------
namespace BFN_DamageCalculator.Schemas {
    
    /// <remarks/>
    [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "0.0.0.0")]
    [System.SerializableAttribute()]
    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.ComponentModel.DesignerCategoryAttribute("code")]
    [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace= "ResistanceCollection")]
    public partial class Resistance {
        private const float LOWEST_MULTIPLIER = 0.1f;
        private const float LOWEST_DIVIDER = 10f;
        private const float LOWEST_RESISTANCE = 0.1f;
        private const float HIGHEST_RESISTANCE = 0.9f;
        private string nameField = "DEFAULT";
        
        private float reductionMultiplier = 0.8f;
        
        private float scalingDivider = 0f;

        public string Name {
            get {
                return this.nameField;
            }
            init {
                if (String.IsNullOrEmpty(value))
                    return;
                this.nameField = value;
            }
        }
        public float ReductionMultiplier
        {
            get => reductionMultiplier;
            init => reductionMultiplier = BindToLowest(value, LOWEST_MULTIPLIER);
        }
        public float Divider {
            get => scalingDivider;
            init => scalingDivider = BindToLowest(value, LOWEST_DIVIDER);
        }
        public float GetEffectPercentage(float armor)
        {
            return BindToResistanceBorders((armor / (armor + scalingDivider)) * reductionMultiplier);
        }
        private float BindToResistanceBorders(float value)
        {
            return Math.Clamp(value, LOWEST_RESISTANCE,HIGHEST_RESISTANCE);
        }
        private float BindToLowest(float value, float lowestValue)
        {
            if (value < lowestValue)
                return lowestValue;
            return value;
        }
    }
}
C#:
    [Test]
    public void Calculate_ReturnsExpectedResult()
    {
        // Arrange
        var resistance = new Resistance() { Divider = 50f, ReductionMultiplier = 0.8f };

        // Act
        var result = resistance.GetEffectPercentage(100f);

        // Assert
        Assert.AreEqual(0.53333f, result, 0.0001f);
    }
    [Test]
    public void BelowLowestMultiplier()
    {
        // Arrange
        var resistance = new Resistance() { Divider = 50f, ReductionMultiplier = -100900000f };

        // Act
        var result = resistance.GetEffectPercentage(100f);

        // Assert
        Assert.AreEqual(0.1f, result, 0.0001f);
    }
    [Test]
    public void BelowLowestDivider()
    {
        // Arrange
        var resistance = new Resistance() { Divider = -1000000000f, ReductionMultiplier = 1f };

        // Act
        var result = resistance.GetEffectPercentage(100f);

        // Assert
        Assert.AreEqual(0.9f, result, 0.0001f);
    }
    [Test]
    public void BelowLowestDividerAndMultiplier()
    {
        // Arrange
        var resistance = new Resistance() { Divider = -1000000000f, ReductionMultiplier = -10000000f };

        // Act
        var result = resistance.GetEffectPercentage(100f);

        // Assert
        Assert.AreEqual(0.1f, result, 0.0001f);
    }
    [Test]
    public void BelowLowestDividerAndMultiplierAndNoArmor()
    {
        // Arrange
        var resistance = new Resistance() { Divider = -1000000000f, ReductionMultiplier = -10000000f };

        // Act
        var result = resistance.GetEffectPercentage(0f);

        // Assert
        Assert.AreEqual(0.1f, result, 0.0001f);
    }
    [Test]
    public void HighDivider()
    {
        // Arrange
        var resistance = new Resistance() { Divider = 1000000000f, ReductionMultiplier = 0.8f };

        // Act
        var result = resistance.GetEffectPercentage(40f);

        // Assert
        Assert.AreEqual(0.1f, result, 0.0001f);
    }
    [Test]
    public void HighMultiplier()
    {
        // Arrange
        var resistance = new Resistance() { Divider = 50f, ReductionMultiplier = 100000000f };

        // Act
        var result = resistance.GetEffectPercentage(40f);

        // Assert
        Assert.AreEqual(0.9f, result, 0.0001f);
    }
    [Test]
    public void HighMultiplierAndDivider()
    {
        // Arrange
        var resistance = new Resistance() { Divider = 100000000f, ReductionMultiplier = 100000000f };

        // Act
        var result = resistance.GetEffectPercentage(40f);

        // Assert
        Assert.AreEqual(0.9f, result, 0.0001f);
    }
 

KonradN

Super-Moderator
Mitarbeiter
Aber noch immer nichts an Dokumentation ... ok - fast nichts. Auf der Klasse hast Du einen remarks Kommentar der aber leer ist.

Schau Dir z.B. mal https://blog.submain.com/c-documentation-start-finish-guide/ an!

Vor allem: Du kannst in Visual Studio vor der Klasse oder vor Methoden einfach /// eintippen und schon bekommst Du ein grobes Skeleton mit Summary, Parametern und return und so.

Und die Arange, Act und Assert Kommentare kannst Du weg lassen. Das ist die Erklärung, wie Unit Tests aufgebaut sind - das muss man nicht ständig dazu schreiben. Du kannst die Blöcke einfach mit Leerzeile trennen und dann hast Du die Struktur ja auch deutlich genug gemacht.
 

AndiE

Top Contributor
Wäre es nicht trotzdem angenehmer, die Bezeichner an so etwas wie eine UswStory zu binden?

Dem Charakter entsteht eine enemyDamage.( durch den Feind ausgelösten Schaden). Durch eine Rüstung wird dieser Schaden deren Stärke armorStrongness reduziert, wobei die Stärke der Verrringerung vom materialFaktor abhängt. Es ergibt sich dann als Ergebnis übrigens die effectiveDamage.

Diese camelCase-Bezeichnung fände ich hilfreich, um das Programm und die Bezeichner zu verstehen.
 
Y

yfons123

Gast
ich habe jetzt nochmal versucht doku zu schreiben
das </remarks> gibts in dotnet gar nicht, das wurde von xsd.exe generiert was auf mono ausgelegt war

ich habe versucht die kommentare halbwegs so zu setzen
C#:
    /// <summary>
    /// Calculates the Effectiveness of an Armor
    /// </summary>
    [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "0.0.0.0")]
    [System.SerializableAttribute()]
    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.ComponentModel.DesignerCategoryAttribute("code")]
    [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace= "ResistanceCollection")]
    public partial class ArmorEffect {
        private const float LOWEST_MULTIPLIER = 0.1f;
        private const float LOWEST_DIVIDER = 10f;
        private const float LOWEST_RESISTANCE = 0.1f;
        private const float HIGHEST_RESISTANCE = 0.9f;

        private string nameField = "DEFAULT";
        private float reductionFactor = 0.8f;
        private float scalingDivider = 50f;
        /// <summary>
        /// This Name links the ArmorType to it's according Resistance Type
        /// </summary>
        public string Name {
            get {
                return this.nameField;
            }
            init {
                if (String.IsNullOrEmpty(value))
                    return;
                this.nameField = value;
            }
        }

        /// <summary>
        /// The <see cref="ReductionMultiplier"/> property is used to define how well an armor scales.
        /// The higher this value is, the the higher is the maximum of reduction.
        /// If the value passed in is less than the <see cref="LOWEST_MULTIPLIER"/>, then the <see cref="LOWEST_MULTIPLIER"/> value will be set.
        /// </summary>
        public float ReductionMultiplier
        {
            get => reductionFactor;
            init => reductionFactor = BindToLowest(value, LOWEST_MULTIPLIER);
        }
        /// <summary>
        /// The Divider property is used to define how fast armor reaches it's approximate maximum.
        /// The lower this value is, the faster it will reach the maximum.>
        /// If the value passed in is less than the <see cref="LOWEST_DIVIDER"/>, then the <see cref="LOWEST_DIVIDER"/> value will be set.
        /// </summary>
        public float Divider {
            get => scalingDivider;
            init => scalingDivider = BindToLowest(value, LOWEST_DIVIDER);
        }
        /// <summary>
        /// Transforms the Armor to the effective Reduction of damage.
        /// This value is Clamped by <see cref="BindToResistanceBorders(float)"/>
        /// </summary>
        /// <param name="armor">The amount of Armor that needs to be transformed to a Percentage</param>
        /// <returns>The Percentage of how much this Armor can reduce</returns>
        public float GetEffectivePercentage(float armor)
        {
            return BindToResistanceBorders((armor / (armor + scalingDivider)) * reductionFactor);
        }
        /// <summary>
        /// clamps the value between <see cref="LOWEST_RESISTANCE"/> and <see cref="HIGHEST_RESISTANCE"/> so it will always be an percentage between these two values.
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        private float BindToResistanceBorders(float value)
        {
            return Math.Clamp(value, LOWEST_RESISTANCE,HIGHEST_RESISTANCE);
        }
        private float BindToLowest(float value, float lowestValue)
        {
            if (value < lowestValue)
                return lowestValue;
            return value;
        }
    }
das mit dem Pascal case ist von C# so gedacht..man sollte das so machen

die xml dazu sieht dann so aus
XML:
<?xml version="1.0" encoding="UTF-8"?>
<ResistanceCollection xmlns="ResistanceCollection">
    <ArmorEffect>
        <Name>Test</Name>
        <ReductionMultiplier>0.5</ReductionMultiplier>
        <Divider>25</Divider>
    </ArmorEffect>

</ResistanceCollection>
 

KonradN

Super-Moderator
Mitarbeiter
Y

yfons123

Gast
ok keine ahnung was ich dann falsch gemacht habe dass es das kommentar nicht angezeigt hat, jetzt gehts und zeigt es auch an... ups
 

AndiE

Top Contributor
Ich finde, dass immer noch getestet wird, ob das Programm richtig rechnet. Und das tut es. Ich würde das mal anders denken.

Der Gegner kann einen Schlag mit einer Stärke von 1 bis 10 ausführen. Der Held hat eine Gesundheit von 0 bis 10. Nach dem Schlag beträgt die Gesundheit bei einem ungeschützten Helden:

neue Gesndheit= alte Gesundheit-Schaden

Trägt der Held eine Rüstung, so wird

neue Gesundheit=alte Gesundheit-(Schaden-Rüstung)

oder

neue Gesundheit=alte Gesundheit-Schutz

wobei neue Gesundheit<=alte Gesundheit.

Dabei ist der Rüstung-Wert in einem Bereich von 0 bis 10 zu finden.

Schutz hat dann auch einen Wertebereich von 0 bis 10.

Man kann nun tatsächlich sagen, dass eine Rüstung aus Kupfer leichte Schläge besser wegsteckt als schwere, jedoch im ganzen weniger Effektiv ist als eine Eisenrüstung. Eine Eisenrüstung steckt sowohl leichte als auch schwere Schläge gut weg.

Und hier kommen die Testfälle ins Spiel. Hier muss man eben eine Formel finden, die das vernünftig abbildet, wenn man die obigen Wertebereiche nutzt.
 
Ähnliche Java Themen
  Titel Forum Antworten Datum
Zrebna Wieso sollte man Null-Prüfungen nicht mit Optional-Objekten nutzen? Allgemeine Java-Themen 13
Zrebna Wieso sind eigentlich JUnit-Tests in src/test/java platziert - nur Konvention? Allgemeine Java-Themen 7
P Wieso benutzen PriorityQueues Heaps? Allgemeine Java-Themen 2
I Wieso funktioniert das nich? Allgemeine Java-Themen 5
F Input/Output NullPointerException, aber wieso? [Apache POI] Allgemeine Java-Themen 11
R MAC-Adresse eindeutig für einen PC ? Bezug zu Netzwerk, wieso ? Allgemeine Java-Themen 7
P Best Practice Wieso funktioniert der Modulo - Operator nicht? Allgemeine Java-Themen 2
J Jasper ireport - wieso beendet die Anwendung wenn ich die Preview schließe Allgemeine Java-Themen 1
I Interface Interface / Klasse - wieso Abstract? Allgemeine Java-Themen 13
A Methoden Generische Methode mit Arrays - Source Compatibility 1.7 benötigt, wieso? Allgemeine Java-Themen 3
S RemoteException wieso ? Allgemeine Java-Themen 6
J if else Anweisung macht nicht was es soll. Wieso? Allgemeine Java-Themen 10
P wieso kann ich auf bluej exportieren aber auf eclipse nicht? Allgemeine Java-Themen 2
DEvent Wieso ist Javadoc mit Html Tags? Allgemeine Java-Themen 47
D java.util.InputMismatchException im Scanner -wieso? Allgemeine Java-Themen 5
E Wieso returnt das hier 1? Allgemeine Java-Themen 3
DStrohma [Erledigt] Wieso kann ich Taste 'ENTER' in JTable nicht belegen? Allgemeine Java-Themen 2
C Wieso funktioniert das? Allgemeine Java-Themen 6
W Wieso funktioniert dieser Code hier? Allgemeine Java-Themen 6
S Wieso stehen in der API immer wieder abstrakte Methoden ? Allgemeine Java-Themen 7
lacyuu Schleife hängt sich auf, wieso?? Allgemeine Java-Themen 2
V Wieso meckert FindBugs da? Allgemeine Java-Themen 7
P Wieso HashMap-Zugriff mit Object, statt mit MyObject? Allgemeine Java-Themen 12
V Wieso Heap Space Problem? Allgemeine Java-Themen 14
J Wieso implementiert HTTPServlet Serializable? Allgemeine Java-Themen 2
P Wieso skalieren diese beiden Threads unterschiedlich gut? Allgemeine Java-Themen 16
zilti Wieso geht der StreamReader/Writer nicht? Allgemeine Java-Themen 5
T Wieso erfolgt keine Ausgabe. /Excel Allgemeine Java-Themen 19
G wieso wird der String des StringBuilder immer länger? Allgemeine Java-Themen 2
G wieso "implements" Allgemeine Java-Themen 13
S Problem mit generics -> ClassCastException und ka wieso Allgemeine Java-Themen 20
G Übergabe funzt nicht, aber wieso? Allgemeine Java-Themen 3
G NullPointer ? wieso? Allgemeine Java-Themen 7
I Wieso läuft Programm bei Kollegen aber nicht bei mir? Allgemeine Java-Themen 10
zilti LZW-gepackte Datei. Ich krieg ne Krise. Allgemeine Java-Themen 8
M Wo krieg' ich die Datei Servlet.jar Allgemeine Java-Themen 3
M Erzeugen einer Jar-Datei - Ich krieg's nit gebacken. Allgemeine Java-Themen 7
W Checkliste Unit Test Allgemeine Java-Themen 17
Y Wie sinnvolle unit tests schreiben Allgemeine Java-Themen 29
sascha-sphw Erste Schritte Unit und Integration-Tests im Java Modul System Allgemeine Java-Themen 10
B Frage zu Unit-Tests Allgemeine Java-Themen 6
J Alle Unit Tests in Maven Modul Projekt ausführen Allgemeine Java-Themen 7
looparda Unit Test - Abgänigkeit zur Datenbank isolieren Allgemeine Java-Themen 3
R Unit Test Allgemeine Java-Themen 1
M Für was schreibt man Unit-Tests? Allgemeine Java-Themen 55
P J-Unit vergleich von 2 Objekten merkwürdig Allgemeine Java-Themen 7
G ThreadLocal in Muster "Unit of Work" Allgemeine Java-Themen 7
K Unit Test consolen ein-/ausgaben. Allgemeine Java-Themen 7
M Html Unit Whitespace-Problem Allgemeine Java-Themen 4
fastjack Unit-Testen mit Mocks Allgemeine Java-Themen 6
Jay_030 Guice: Frage im Umgang mit Unit-Tests Allgemeine Java-Themen 4
B FileWriter / FileReader testen / Mock-Objekt für Unit Tests? Allgemeine Java-Themen 6
S Unit Testing mit JMock Allgemeine Java-Themen 11
alexpetri unit tests für pdfs Allgemeine Java-Themen 4
B J-Unit Tests. Alle Tests eines Package einsammen. Allgemeine Java-Themen 4
tfa Unit-Tests für private Methoden Allgemeine Java-Themen 25
W Unit Tests im "Hauptprojekt" oder in Modulen Allgemeine Java-Themen 3
M Eine Frage über Unit-Tests mit Java Allgemeine Java-Themen 2
N Ausgaben (System.out) umlenken und in Unit-Tests überprüfen? Allgemeine Java-Themen 2

Ähnliche Java Themen

Neue Themen


Oben