@N1ls: Was genau meinst du? Das mit dem statischen "Create" oder dass die Syntax dafür möglichst kurz sein soll? Für das "Create" habe ich jedenfalls einen praktischen Nutzen, ist allerdings etwas aufwändiger zum erklären. Und Delphi kann ich auf jeden Fall mal nicht^^
Ich meine eher das statische Create. Kurz wird's ja schon, wenn ich nur
A myA = new A()
schreibe.
Konstruktoren sind eh statisch. Also wieso diese Umständlichkeiten?
Grüsse,
N1ls
Hallo,
gibt es für soetwas irgendeinen praktischen Nutzen? Tschuldigung, dass ich nachfrage. Aber für mich sieht das irgendwie so aus, als ob Du C# eine Delphi-Syntax aufzwingen willst...
Grüsse,
N1ls
Hallo Herbivore,
Hallo TheBrainiac,
deine Lösung ist korrekt.
...Hier zum Vergleich noch, wie ich mir das vorgestellt hatte.
...
Ich halte beide Lösungen für relativ schwer lesbar. Wirklich korrekt sind sie auch nicht. Ein Kniffel ist immer auch ein FullHouse, das berücksichtigen beide Varianten nicht. Das lässt sich natürlich einfach korrigieren, steigert aber die Verständlichkeit des Codes nicht wirklich.
MfG,
N1ls
Das ist Unsinn. StringBuilder dient bei Plattformaufrufen als beschreibbarer Puffer. [...]
Wenn du eine konstante Zeichenkette als Parameter übergibst oder als Rückgabewert bekommst kann dieser als string deklariert werden.
Wenn das so unsinnig ist, wäre es ja schön gewesen, Du hättest auch mal ein entsprechendes Beispiel mit einem String-Rückgabewert aufgeführt.
MfG,
N1ls
Hallo And.Wolf,
[DllImport("mydll.dll", SetLastError = true,CharSet = CharSet.Ansi)]
private static extern StringBuilder MyFunction(string Par);
versuch es mal so. Hab's jetzt nicht ausprobiert. Ich weiss auf jeden Fall, dass Du einmal den Stringbuilder für den Rückgabewert nutzen musst, einen einfachen String für den Übergabeparameter. Klingt erstmal komisch, ist aber so 😉
Evtl. musst Du noch die Calling Convention im Delphi Code auf StdCall anpassen.
Grüsse,
N1ls
Hallo CSL,
Wenn man erst die UI macht besteht aber die Gefahr das man u.u:
- "mal eben" eine Funktionalität ein hackt,
- das man UnitTests weg lässt um die UI "schnell mal eben" mit Funktionalität aus zu statten,
- das man weniger Abstrahiert sondern direkt mit der UI verknüpft (Ruf die Komponente einfach mal schnell auf()
Da gebe ich Dir vollkommen Recht. Dazu gehört dann natürlich Disziplin und sicherlich auch Überzeugungsarbeit bei so manchem Chef, um gegen diese Gefahren zu arbeiten. Aber das ist doch auch ein Stück, worauf man hinaus will, wenn man Test-Driven entwickeln als sinnvoll ansieht, oder?
Das UI steht, es ist bekannt welche TextEdits es gibt, welche Checkboxen, Radiobuttons etc. Jetzt kann man an die Logik des UI gehen und diese Test-Driven entwickeln - quasi per Unit-Tests Stück für Stück das Klasse aufbauen, die die Logik des UI widerspiegelt.
- das man Dummy Templates erstellt die die Funktionalität erst viel Später bekommen (wichtige Zeit verloren),
Man muss ja nicht zwangsweise das komplette UI sofort erstellen. Wenn sich Teile sinnvoll abgrenzen lassen, kann man da sicherlich schrittweise vorgehen.
- und zu guter letzt das man die UI immer wieder umbauen muss da sich bei der Implementation der Funktionalität neue Erkenntnisse ergaben.
Genau das bezweifel ich. Das UI entwickele ich nach den Anforderungen und Wünschen des Kunden. Wenn dieser damit zufrieden ist, dann interessiert ihn nicht mehr, was dahinter liegt. Wenn bei der Implementierung der Funktionalitäten dann Probleme auftauchen, heisst das entsprechend, ich kann Anforderungen respektive Wünschen nicht mehr gerecht werden. Dann habe ich aber ein ganz anderes Problem als den Umbau des UI. Das sollte aber der Ausnahmefall sein.
Grüsse,
N1ls
Hallo herbivore,
Alleine die Auffassung "nur noch eine UI davor packen" widerspricht jeglichen agilen Prinzipien. Und TDD kommt nun einmal aus genau dieser Richtung.
TDD sagt erstmal nichts darüber aus, ob man Top-Down oder Bottom-Up entwickelt. Es sagt nur, dass du den Test vor der Implementierung schreibst.
Ich habe nicht behauptet, dass TDD eine Richtung vorgibt. Es kommt aber aus der agilen Softwareentwicklung und dort geht es u.A. darum kontinuierlich Nutzen zu produzieren. Das kann ich eben nicht, wenn ich zuerst mit TDD das komplette Backend fertige und dann das UI anfange. Im schlimmsten Falle ist Release-Termin, wenn ich mit dem Backend gerade durch bin... das heisst dann, ich habe noch überhaupt keine Funktionalität geschaffen, jedenfalls keine für den Kunden nutzbare.
Gerade wenn du sagst, dass bei der Entwicklung "vom User Interface in Richtung Backend" "seltener nachträgliche Änderungen gewünscht werden", bedeutet das doch, dass das das gewünschte Verhalten (gerade auf den oberen, gui-nahen Ebenen) von Anfang an feststeht (und sich nicht mehr ändert). Das sind doch optimale Voraussetzungen, um dieses Verhalten in Forms von Tests auszudrücken und damit auch zu fixieren. Also nochmal, nur weil du TDD verwendest, musst du nicht Bottom-Up arbeiten.
Das ist ja genau meine Meinung. Und so gehe ich bzw. versuche ich möglichst vorzugehen. Deshalb finde ich es auch Schade in Bezug auf TDD etwas zu lesen wie
Man löst Probleme auch von der anderen Richtung, ich für mein Fall hatte oft mit der UI angefangen und dann stück für stück zur Funktionalität vor gearbeitet. Jetzt erstell ich erst die Funktionalität und muss dann nur noch eine UI davor packen.
Bei mir hat sich dieses Bewusstsein, immer vom UI aus zu arbeiten, eigentlich letztes Jahr auf der Advanced Developers Conference gefestigt. In bestimmt 4 oder 5 der von mir besuchten Sessions wurde genau dieses Vorgehen empfohlen, ebenso im anschliessenden Workshop mit Ralf Westphal. Gegenteilige Meinungen habe ich nicht mitbekommen.
Genau daran denke ich jetzt immer, wenn ich von Kollegen höre "das können wir jetzt nicht so einfach ändern, dann müsste ich das ganze Backend umstellen". Dieser Satz taucht immer auf, wenn man dem Kunden zum ersten Mal das UI vorstellt...
Grüsse,
N1ls
Man löst Probleme auch von der anderen Richtung, ich für mein Fall hatte oft mit der UI angefangen und dann stück für stück zur Funktionalität vor gearbeitet. Jetzt erstell ich erst die Funktionalität und muss dann nur noch eine UI davor packen.
Wenn TDD zu einem solchen Verhalten führt, dann werden dadurch natürlich erhebliche Mehrkosten verursacht. Entwicklung vom User Interface in Richtung Backend ist effizient, weil
*keine Funktionalität implementiert wird, die nicht benötigt wird
*seltener nachträgliche Änderungen gewünscht werden(wenn das UI vom Kunden abgenommen ist, man im Backend relativ frei)
Alleine die Auffassung "nur noch eine UI davor packen" widerspricht jeglichen agilen Prinzipien. Und TDD kommt nun einmal aus genau dieser Richtung.
Von daher finde ich es natürlich falsch, hier im Thread zu suggerieren, TDD wuerde die Entwicklungsweise in dieser Hinsicht quasi umkehren.
Da ist eben der unterschied, unser Vor/Zurück hat keine Logik die ein Button deaktiviert oder ähnliches, ...
Genau DAS meinte ich. Wenn keine Logik, dann ist es sinnlos mit Vor- und Zurück-Buttons zu arbeiten. Mindestanforderung, um soetwas einem TabControl vorzuziehen ist eine notwendige Sequenz.
Ich würde aber vor schlagen das Thema hier sein zu lassen, da das nicht mehr zum Thema gehört.
Ich denke diese gehört eng zusammen(wieso sonst sollte es hier aufgetaucht sein?). Der Nutzen von Unit-Test hängt auch mit dem Verständnis von verschiedenen Controls zusammen. Ich muss zum Teil die Business-Logik aus diesen Controls rausziehen, um wirklichen Nutzen aus TDD oder überhaupt Unit-Tests ziehen zu können.
MfG,
N1ls
Wie wär es damit, gerade gefunden in mein TDD Material:
[TestMethod] public void Plus_FileDollarPlusFiveDollar_ReturnsSumExpression() { Money five = Money.Dollar(5); Money six = Money.Dollar(6); IExpression result = five.Plus(six); Sum sum = (Sum)result; Assert.AreEqual(five, sum.Augend); Assert.AreEqual(six, sum.Addend); }
"Sum" ist eine Expression die die beiden Zahlenwerte enthält, hier wurde überprüft das "Plus" korrekt "Sum" erstellt hat.
Das in zwei Tests wäre schon recht absurd.
Das wäre schonmal ein Beispiel, wo der Testname nicht passt("Plus_FiveDollarPlusSixDollar_ReturnsSumExpression" <- mal die vertipper direkt bereinigt). Ein Test für diesen Testnamen müsste ja eigentlich folgendermassen aussehen:
public void Plus_FiveDollarPlusSixDollar_ReturnsSumExpression()
{
Money five = Money.Dollar(5);
Money six = Money.Dollar(6);
IExpression result = five.Plus(six);
Assert.IsTrue(result is Sum);
}
Das finde ich erstens einen guten Test, um sicherzustellen, dass das IExpression, was von der Plus-Methode zurückgeliefert wird, wirklich vom Typ Sum ist und zweitens prüft der Test nun genau das, was in seinem Namen steckt.
Aber wie weiter? Kommutativgesetz lassen wir mal aussen vor. Nehmen wir an, die Reihenfolge der Summanden wäre wichtig. Dann käme ich zu dem Testnamen "Plus_FiveDollarPlusSixDollar_ReturnsSumWithAugendFiveAndAddendSix". Das "And" macht mir da wieder Sorgen. Was würde es bedeuten, dies wirklich wieder auf 2 Tests aufzusplitten?
private Sum GetSumAddFiveDollarAndSixDollar()
{
Money five = Money.Dollar(5);
Money six = Money.Dollar(6);
return (Sum)(five.Plus(six));
}
public void Plus_FiveDollarPlusSixDollar_SumWithAugendFive()
{
Sum s = GetSumAddFiveDollarAndSixDollar();
Assert.AreEqual(5, s.Augend);
}
public void Plus_FiveDollarPlusSixDollar_SumWithAddendSix()
{
Sum s = GetSumAddFiveDollarAndSixDollar();
Assert.AreEqual(5, s.Addend);
}
Jetzt wird eigentlich auch klar, dass die Prüfung auf ein Sum-Objekt bei der Plus-Methode gar nicht mehr notwendig ist(man könnte sie dennoch beibehalten), dies wird implizit mitgeprüft.
Ist das jetzt wirklich absurd, daraus 2 Testcases zu machen? Ich prüfe auf jeden Fall auch zwei unterschiedliche Dinge: Die Werte und deren Reihenfolge.
Dazu fällt mir etwas ein, habe ich gerade in einem Projekt gesehen.
Ich bekomme von einer Methode eine Liste von Dokumenten, die Dokumente wiederum haben eine Liste von Sprachen.
Ich muss prüfen das die Sprachen alle korrekt sind, also Gültige Sprachen.
Ich hatte zu dem Zweck einfach die Dokumente geloop und in dem loop das Assert per Linq auf die Sprach liste los gelassen.
Wie würde man so etwas korrekt Testen?
Dann bewegen wir uns schon gar nicht mehr im Bereich von Unit-Tests. Das sind Integrationstests. 😉
MfG,
N1ls
Hallo CSL,
ich würds wie weiter oben geschrieben halten:
Zitat von: dN!3L
Oder mit R. Osheroves Worten: "Wenn du Probleme hast, einen Test zu benennen, testet er zu viel".
Das ist sicherlich ein gutes Kriterium, alleine aber zu wenig. Gerade bei Anfängern in Sachen Unit-Tests merke ich häufig, dass diese überhaupt keine Probleme haben einen Namen für einen Test zu finden. Beispielsweise finde ich da bei einem Stack Testmethoden a la "TestPushElement". Das sieht für einen Anfänger total unproblematisch und nach einem guten Namen aus. Und innerhalb solcher Tests wird dann natürlich auf alles, was sich bewegt, mit Asserts geschossen.
Voraussetzung ist also m.E. nach erstmal, dass es Namenskonventionen für Tests gibt. Darin muss sich inhaltlich wiederspiegeln, was getan wird und was erwartet wird. Erst dann lässt sich obiges Kriterium sinnvoll anwenden.
Aber es ist irgendwie immer noch nicht ausreichend. Bleiben wir mal beim Stack:
public Push_OneElement_ElementIsTopElementOnTheStack()
{
// Arrange
var sut = new Stack<int>();
// Act
sut.Push(2);
// Assert
Assert.AreEqual(1, sut.Count);
Assert.AreEqual(2, sut.Peek());
}
So (ähnlich) hätte ich das bis vor kurzem als Test implementiert. Ich hatte auch kein Problem damit, einen Namen für den Test zu finden. Dennoch würde ich heute sagen, es wird eindeutig zuviel getestet.
Was würde jetzt passieren, wenn im Code jedes Element versehentlich doppelt auf dem Stack landet? Der Test würde fehlschlagen mit der Meldung, dass 1 erwartet wurde, jedoch der tatsächliche Wert 2 ist. Hier wird schon deutlich, dass ich mir die optionalen Meldungen der Asserts nicht mehr sparen kann, wenn ich mehrere innerhalb eines Tests habe.
Viel schlimmer aber noch ist, dass ich hier im Testprotokoll quasi belogen werde. Wenn der Test fehlschlägt, heisst das die Erwartung "ElementIsTopElementOnTheStack" ist nicht erfüllt. Stimmt aber nicht, das oberste Element entspricht nämlich dem hinzugefügten Element.
Vielleicht ist das nicht für jeden (sofort) nachvollziehbar, aber ich fühle mich da mittlerweile mit einem extra Test "Push_OneElement_ElementCountIncreasesByOne" bedeutend besser.
Und beim Durchschauen meiner Tests geht mir das irgendwie bei jedem Test so, der mehr als ein Assert enthält. Entweder komme ich zu dem Schluss, die getesteten Dinge sind zu unterschiedlich, um in einen Test zu gehören(hier die Größe des Stacks und der Inhalt des Stacks), oder ich bin nicht damit zufrieden, wie sich das im Protokoll der Testausführung liest.
Ich bin immer noch nicht an dem Punkt, dass ich mehr als ein Assert per se verteufeln würde. Aber da müssen schon wirklich gute Gründe sprechen. Wäre schön, wenn hier jemand mal ein Beispiel hätte, wo man partout nicht ohne mehrere Asserts auskommt.
MfG,
N1ls