Thomas Bandt

Über mich | Kontakt | Archiv

TDD in der (meiner) Praxis – Wunsch und Wirklichkeit

Zunächst einmal: Test Driven Development, wozu ich insbesondere „test first“ zähle, hat für mich einen gewissen Charme. Es ist zweifelsfrei so, dass dadurch die Architektur der eigenen Anwendung verbessert wird und viele kleine Fehler vermieden werden können. Der Teufel steckt ja bekanntlich im Detail. Umso besser, wenn man nun ein wirksames Werkzeug gegen ihn hat.

Die Frage ist nur: zu welchem Preis?

Nehmen wir mal eine Situation aus meinem Alltag. Ein potentieller Kunde fragt bei mir die Erstellung einer Webapplikation an, die ein wenig vom Standard abweicht, den ich mit meinen Komponenten abdecken kann. Individualprogrammierung ist also gefragt.

Nun kann ich den Aufwand in Stunden kalkulieren, ein paar Prozent Puffer draufschlagen und das Ganze mit meinem Stundensatz multiplizieren und erhalte in etwa den Preis, den ich dem Kunden machen kann.

Üblicherweise bewegen sich die Aufwände in meinen Alltagsprojekten dann zwischen 40 und 120 Stunden. Setze ich nun strikt auf TDD, muss ich hier von Anfang an mehr Zeit einkalkulieren – trotz inzwischen fast zweijähriger Praxis würde ich diesen Mehraufwand irgendwo zwischen 10 und 30% ansetzen.

Sind wir bei 100 Stunden und einem fiktiven Stundensatz von 80 EUR also bei 8.000 EUR, lande ich mit einer realistischen Kalkulation und TDD schon bei 10.000 EUR.

Nun ist das eine schöne Milchmädchenrechnung. Was zählt ist aber die reale Welt – und in dieser konkurriere ich mit meist gleich mehreren Marktteilnehmern, deren Qualitätsansprüche an die eigene Software ich nur erahnen kann (In diesen Budgetregionen konkurriert man im Zweifel auch häufiger mit PHP-Scriptern). Und klar gibt es Ausnahmesituationen in denen es keinen Sinn für den Kunden macht wg. 2.000 EUR zu entscheiden, da sollten andere Kriterien mehr zählen – aber nicht jeder Kunde tickt so.

Dann kommt zwangsläufig die Frage auf: welchen Wert hat es für mich, auf TDD zu beharren? Oder es gar selbst zu finanzieren, also auf die 30% Aufschlag zu verzichten und es „einfach zu machen“?

Von einem gebauchpinselten Ego kann ich mir am Ende des Monats nichts kaufen. Und den wesentlich besseren Code, den ich durch TDD ohne Frage erhalte, sehe ich in 80 von 100 Fällen sowieso nie wieder, da in diesen Fällen im Lebenszyklus der Anwendung höchstens einmal kosmetische Anpassungen fällig werden – bis die App nach ein paar Jahren verrichteten Dienstes einfach weggeworfen wird. Und selbst dort, wo die Anwendung kontinuierlich weiterentwickelt wird und Refactoring an der Tagesordnung ist, kann es sein, dass der Kunde Hautausschlag bekommt, wenn auf der Rechnung dafür auch nur irgendetwas mit „Test“ draufsteht – O-Ton: „Das ist für mich selbstverständlich!“.

Und wenn man die Augen aufmacht und mal sieht was es alles so an Technologien da draußen gab, gibt und geben wird, mit denen Software entwickelt wird – da muss man sich doch wirklich fragen, ob man nicht auf einem etwas zu hohen Ross sitzt? Schließlich werden auch heute noch Anwendungen mit Access, Filemaker, und was es nicht sonsts noch so an Verbrechen gibt, erstellt, betreut und von den Kunden begeistert oder wenigstens zufrieden verwendet. Obwohl es jedem von uns vermutlich den Magen umdreht, wenn man mal unter die Haube schaut.

Und nicht zuletzt muss man auch sagen, dass früher ja nun nicht alles schlecht war. Ich habe auch vor der Verwendung von TDD bereits robuste und gute Software entwickelt und ich kann nicht sagen, dass ich bei jedem Projekt im Anschluss erstmal lange Zeit für Bugfixing benötigt habe. Natürlich ist der ein oder andere Fehler erst beim Klicken durch die (Web-) Anwendung aufgetaucht – aber, so what?

Und den Versuch die Architektur meiner Anwendungen zu verbessern, unternehme ich auch nicht erst seit TDD. Sicher arbeite ich nun mit wesentlich weniger Koppelungen, mache mir noch mehr Gedanken im Detail – aber ich mache noch immer Fehler oder treffe (Design-)Entscheidungen, die ich so schon beim nächsten Projekt nicht mehr treffen würde. Das Rad dreht sich eben ständig weiter. Das heißt der alte Spruch „Bitte lass mich mit meinem Code von vor zwei Jahren in Ruhe“ wird so auch mit TDD nicht an Gültigkeit verlieren.

Zum Abschluss noch etwas Konkretes. Aufgabe: in der Datenbank werden Aufgabenlisten zu einem Projekt gespeichert. Es soll ein Formular erstellt werden, über welches diese Listen erzeugt werden. Die Aufgabenlisten sind wirklich simpel:

   1:  public class TaskList : EntityBase
   2:  {
   3:      public int ProjectID { get; set; }
   4:      public string Name { get; set; }
   5:      public string Description { get; set; }
   6:  }

Ohne TDD wäre das vermutlich eine Sache von 10-15 Minuten. Mit TDD und der damit verbundenen Zeremonien ... tja, ich glaube es war etwas mehr als eine dreiviertel Stunde.

Controller Tests (70 Lines of Code)

   1:  [TestFixture]
   2:  [Category("Blubr.App.Areas.Project.Controllers.TasksController")]
   3:  public class Wenn_eine_Aufgabenliste_angelegt_werden_soll : 
   4:      ConcernOfTasksController
   5:  {
   6:   
   7:      private ITaskService TaskService;
   8:   
   9:      public override void Setup()
  10:      {
  11:              
  12:          base.Setup();
  13:   
  14:          TaskService = MockRepository.DynamicMock<ITaskService>();
  15:   
  16:          Sut = new TasksController(TaskService);
  17:          Sut.SetCurrentProject(new Domain.Model.Project { ID = 69 });
  18:   
  19:          using(MockRepository.Record())
  20:          {
  21:              Expect.Call(TaskService.CreateTaskList(Arg<TaskList>.Matches(l => l.Name == "Valid"), 
  22:                  Arg<IValidationState>.Is.Anything)).Return(true);
  23:              Expect.Call(TaskService.CreateTaskList(Arg<TaskList>.Matches(l => l.Name == "Invalid"), 
  24:                  Arg<IValidationState>.Is.Anything)).Return(false);
  25:          }
  26:   
  27:      }
  28:   
  29:      [Test]
  30:      public void Wird_der_Name_gemappt()
  31:      {
  32:          Sut.CreateList(new TaskListViewModel { Name = "testname" });
  33:          TaskService.AssertWasCalled(s => 
  34:              s.CreateTaskList(Arg<TaskList>.Matches(l => l.Name == "testname"), 
  35:              Arg<IValidationState>.Is.Anything));
  36:      }
  37:   
  38:      [Test]
  39:      public void Wird_die_Beschreibung_gemappt()
  40:      {
  41:          Sut.CreateList(new TaskListViewModel { Description = "desc" });
  42:          TaskService.AssertWasCalled(s => 
  43:              s.CreateTaskList(Arg<TaskList>.Matches(l => l.Description == "desc"), 
  44:              Arg<IValidationState>.Is.Anything));
  45:      }
  46:   
  47:      [Test]
  48:      public void Wird_die_ProjektID_gemappt()
  49:      {
  50:          Sut.CreateList(new TaskListViewModel { Description = "desc" });
  51:          TaskService.AssertWasCalled(s => 
  52:              s.CreateTaskList(Arg<TaskList>.Matches(l => l.ProjectID == 69), 
  53:              Arg<IValidationState>.Is.Anything));
  54:      }
  55:   
  56:      [Test]
  57:      public void Wird_zur_Übersicht_weitergeleitet_wenn_alles_geklappt_hat()
  58:      {
  59:          var result = (RedirectToRouteResult) Sut.CreateList(new TaskListViewModel { Name = "Valid" });
  60:          Assert.AreEqual("index", result.RouteValues["action"]);
  61:      }
  62:   
  63:      [Test]
  64:      public void Wird_das_Model_zurückgegeben_wen_nein_Fehler_aufgetreten_ist()
  65:      {
  66:          var result = (ViewResult)Sut.CreateList(new TaskListViewModel { Name = "Invalid" });
  67:          Assert.AreEqual("Invalid", ((TaskListViewModel) result.ViewData.Model).Name);
  68:      }
  69:   
  70:  }

Controller Implementierung (16 LOC)

   1:  [HttpPost]
   2:  [ValidateAntiForgeryToken]
   3:  public ActionResult CreateList(TaskListViewModel model)
   4:  {
   5:   
   6:      var taskList = new TaskList();
   7:      taskList.ProjectID = CurrentProject.ID;
   8:      taskList.Name = model.Name;
   9:      taskList.Description = model.Description;
  10:   
  11:      if (TaskService.CreateTaskList(taskList, new ViewModelValidationState(ModelState)))
  12:          return RedirectToAction("index");
  13:   
  14:      return View(model);
  15:   
  16:  }

Service Tests (99 LOC)

   1:  #region Wenn eine Aufgabenliste validiert wird
   2:   
   3:  [TestFixture]
   4:  [Category("Blubr.Domain.Services.TaskService")]
   5:  public class Wenn_eine_Aufgabenliste_validiert_wird : ConcernOf<TaskService>
   6:  {
   7:   
   8:      public override void Setup()
   9:      {
  10:          Sut = new TaskService(Arg<ITaskRepository>.Is.Anything);
  11:      }
  12:   
  13:      [Test]
  14:      public void Darf_der_Name_nicht_NullOrWhitespace_sein()
  15:      {
  16:          var validationState = new MockValidationState();
  17:          Sut.ValidateTaskList(new TaskList { ProjectID = 1, Name = "" }, validationState);
  18:          Assert.IsTrue(validationState.ContainsKey<TaskList>(l => l.Name));
  19:      }
  20:   
  21:      [Test]
  22:      [ExpectedException(typeof(ArgumentException))]
  23:      public void Muss_die_ProjektID_angegeben_sein()
  24:      {
  25:          var validationState = new MockValidationState();
  26:          Sut.ValidateTaskList(new TaskList { ProjectID = 0 }, validationState);
  27:      }
  28:   
  29:      [Test]
  30:      public void Darf_die_Beschreibung_nicht_länger_als_500_Zeichen_lang_sein()
  31:      {
  32:          throw new NotImplementedException();
  33:      }
  34:   
  35:      [Test]
  36:      public void Darf_der_Name_nicht_länger_als_100_Zeichen_lang_sein()
  37:      {
  38:          throw new NotImplementedException();
  39:      }
  40:          
  41:  }
  42:   
  43:  #endregion
  44:   
  45:  #region Wenn eine Aufgabenliste angelegt werden soll
  46:   
  47:  [TestFixture]
  48:  [Category("Blubr.Domain.Services.TaskService")]
  49:  public class Wenn_eine_Aufgabenliste_angelegt_werden_soll : ConcernOf<TaskService>
  50:  {
  51:   
  52:      private ITaskRepository TaskRepository;
  53:   
  54:      public override void Setup()
  55:      {
  56:          TaskRepository = MockRepository.DynamicMock<ITaskRepository>();
  57:          MockRepository.ReplayAll();
  58:          Sut = new TaskService(TaskRepository);
  59:      }
  60:   
  61:      [Test]
  62:      public void Wird_true_zurückgegeben_wenn_alles_geklappt_hat()
  63:      {
  64:          var result = Sut.CreateTaskList(new TaskList { ProjectID = 1, Name = "Valid" }, new MockValidationState());
  65:          Assert.IsTrue(result);
  66:      }
  67:   
  68:      [Test]
  69:      public void Wird_die_übergebene_Liste_validiert()
  70:      {
  71:          var validationState = new MockValidationState();
  72:          Sut.CreateTaskList(new TaskList { ProjectID = 1, Name = null }, validationState);
  73:          Assert.IsTrue(validationState.ContainsKey<TaskList>(l => l.Name));
  74:      }
  75:   
  76:      [Test]
  77:      public void Wird_false_zurückgegeben_wenn_ein_Fehler_aufgetreten_ist()
  78:      {
  79:          var result = Sut.CreateTaskList(new TaskList { ProjectID = 1, Name = null }, new MockValidationState());
  80:          Assert.IsFalse(result);
  81:      }
  82:   
  83:      [Test]
  84:      public void Wird_das_Erstellungsdatum_gesetzt_wenn_alles_ok_ist()
  85:      {
  86:          Sut.CreateTaskList(new TaskList { ProjectID = 1, Name = "Valid" }, new MockValidationState());
  87:          TaskRepository.AssertWasCalled(r => r.CreateTaskList(Arg<TaskList>.Matches(l => l.Created.Date == Sut.DateTime.Date)));
  88:      }
  89:   
  90:      [Test]
  91:      public void Wird_die_Liste_im_Repository_gespeichert_wenn_alles_ok_ist()
  92:      {
  93:          Sut.CreateTaskList(new TaskList { ProjectID = 1, Name = "Valid" }, new MockValidationState());
  94:          TaskRepository.AssertWasCalled(r => r.CreateTaskList(Arg<TaskList>.Is.Anything));
  95:      }
  96:   
  97:  }
  98:   
  99:  #endregion

Service Implementierung (37 LOC)

   1:  public class TaskService : ServiceBase, ITaskService
   2:  {
   3:   
   4:      public TaskService(ITaskRepository taskRepository)
   5:      {
   6:          TaskRepository = taskRepository;
   7:      }
   8:   
   9:      private readonly ITaskRepository TaskRepository;
  10:   
  11:      public bool ValidateTaskList(TaskList taskList, IValidationState validationState)
  12:      {
  13:   
  14:          if (taskList.ProjectID == 0)
  15:              throw new ArgumentException("Projekt-ID nicht gesetzt");
  16:   
  17:          if(string.IsNullOrWhiteSpace(taskList.Name))
  18:              validationState.AddError<TaskList>(l => l.Name, "Bitte wählen Sie einen Namen für die Aufgabenliste.");
  19:   
  20:          return validationState.IsValid;
  21:   
  22:      }
  23:   
  24:      public bool CreateTaskList(TaskList taskList, IValidationState validationState)
  25:      {
  26:   
  27:          if (!ValidateTaskList(taskList, validationState))
  28:              return false;
  29:   
  30:          taskList.Created = DateTime;
  31:          taskList.ID = TaskRepository.CreateTaskList(taskList);
  32:              
  33:          return true;
  34:   
  35:      }
  36:   
  37:  }

Repository Tests (66 LOC)

   1:  #region Wenn eine Aufgabenliste angelegt wird
   2:   
   3:  [TestFixture]
   4:  [Category("Blubr.Domain.Data.SqlServer.TaskRepository")]
   5:  public class Wenn_eine_Aufgabenliste_angelegt_wird : ConcernOfSqlServerRepository<TaskRepository, Model.TaskList, TaskList>
   6:  {
   7:   
   8:      public override void FixtureSetUp()
   9:      {
  10:          Sut = new TaskRepository();
  11:          InitializeDatabase();
  12:      }
  13:   
  14:      public Model.TaskList DummyTaskList()
  15:      {
  16:          var list = new Model.TaskList();
  17:          list.Description = "Text";
  18:          list.Name = "Name";
  19:          list.ProjectID = 69;
  20:          list.Created = DateTime.Now;
  21:          return list;
  22:      }
  23:   
  24:      [Test]
  25:      public void Wird_die_ID_zurückgegeben()
  26:      {
  27:          var id = Sut.CreateTaskList(DummyTaskList());
  28:          Assert.AreNotEqual(0, id);
  29:      }
  30:   
  31:      [Test]
  32:      public void Wird_der_Name_gespeichert()
  33:      {
  34:          var id = Sut.CreateTaskList(DummyTaskList());
  35:          var list = Sut.Database.TaskLists.Single(l => l.ID == id);
  36:          Assert.AreEqual("Name", list.Name);
  37:      }
  38:   
  39:      [Test]
  40:      public void Wird_die_Beschreibung_gespeichert()
  41:      {
  42:          var id = Sut.CreateTaskList(DummyTaskList());
  43:          var list = Sut.Database.TaskLists.Single(l => l.ID == id);
  44:          Assert.AreEqual("Text", list.Description);
  45:      }
  46:   
  47:      [Test]
  48:      public void Wird_die_ProjektID_gespeichert()
  49:      {
  50:          var id = Sut.CreateTaskList(DummyTaskList());
  51:          var list = Sut.Database.TaskLists.Single(l => l.ID == id);
  52:          Assert.AreEqual(69, list.FKProjectID);
  53:      }
  54:   
  55:      [Test]
  56:      public void Wird_das_Erstellungsdatum_gespeichert()
  57:      {
  58:          var dummy = DummyTaskList();
  59:          var id = Sut.CreateTaskList(dummy);
  60:          var list = Sut.Database.TaskLists.Single(l => l.ID == id);
  61:          Assert.AreEqual(dummy.Created, list.Created);
  62:      }
  63:   
  64:  }
  65:   
  66:  #endregion

Repository Implementierung (21 LOC)

   1:  public class TaskRepository : RepositoryBase<Model.TaskList, TaskList>, ITaskRepository
   2:  {
   3:   
   4:      public int CreateTaskList(Model.TaskList taskList)
   5:      {
   6:          var newTaskList = new TaskList();
   7:          newTaskList.Created = taskList.Created;
   8:          newTaskList.Name = taskList.Name;
   9:          newTaskList.FKProjectID = taskList.ProjectID;
  10:          newTaskList.Description = taskList.Description;
  11:          Database.TaskLists.InsertOnSubmit(newTaskList);
  12:          Database.SubmitChanges();
  13:          return newTaskList.ID;
  14:      }
  15:   
  16:      public override Model.TaskList Map(TaskList source)
  17:      {
  18:          throw new NotImplementedException();
  19:      }
  20:   
  21:  }

Mein (Zwischen-)Fazit:

TDD ist cool, TDD ist nützlich, TDD verhilft zu besserer Software. Aber ist TDD auch alltagstauglich? Sollte ich es konsequent für alle Projekte, die jenseits von C:\Development\Tests\WebsiteProject12 hinausgehen einsetzen? Oder sollte ich zu alter Produktivität zurückkehren, ein paar mehr Fehler in Kauf nehmen? Die Antwort muss ich euch schuldig bleiben - mir schwirrt da vieles durch den Kopf, eine Entscheidung habe ich noch nicht getroffen.

Kommentare

  1. Alex schrieb am Samstag, 17. Juli 2010 15:41:00 Uhr:

    Ähnliche Sorgen plagen mich auch immer wieder.

    Ich stelle aber auch fest, dass z.B. die xUnit.BddExtensions dank Automocking noch weniger Code für das Mocken notwendig machen. Aber auch da muss man sich erst wieder reinfinden, an echten Projekten damit Erfahrungen sammeln (siehe Twitter).

    Im aktuellen Projekt habe ich viele COM-Objekte (praktisch nur). Da artet allein das Mocken derart aus, dass es nicht mehr feierlich ist.

    Aber ich hoffe dennoch, dass sich das im Laufe des Projekts noch amortisiert, denn es wird sicher keine Software, die nur eine Version mit kleinen Folge-Anpassungen hat, sondern konstant weiterentwickelt werden muss.

    Das Problem liegt, denke ich, nach wie vor ein Stück weit auch beim Kunden.
    Wie Du bereits geschrieben hast, geht der Kunde davon aus, dass die Software getestet wurde. Über das "wie" macht er sich sicher keine Gedanken. Genau hier liegt aber der Preisunterschied.

    Wenn die Preise gravierend auseinander liegen, dürfte klar sein, wer testet und wer nicht (soviel Verstand muss auch jeder Kunde besitzen).

    Was aber, wenn der Preis nur im Bereich von 0-20% differiert?

    Die Frage für mich ist: schaffe ich es, TDD bei mir selbst so als Standard zu etablieren, dass ich irgendwann hierfür überhaupt keinen Mehraufwand mehr einplanen muss?

    Den Mehraufwand = 0 zu bringen, ist sicher unrealistisch, allein schon deshalb, weil ich ja die Test-Projekte/-Files anlegen und mit Inhalt füllen muss.

    Ein Ansatz wär, den Testaufwand in kritschen Bereichen der App stärker zu betreiben, als in weniger kritischen.

    Letzlich muss die Entscheidung pro oder contra TDD jeder für sich selbst (und aus voller Überzeugung) treffen, Dogmatismus hilft hier, wie so oft, sicher nicht weiter.
  2. Timur Zanagar schrieb am Samstag, 17. Juli 2010 15:46:00 Uhr:

    Hallo Thomas,

    Genau vor diesen Gedanken stehe ich selber und es ist wirklich so, dass es im Enddefekt heißt "it depends". Auch wenn TDD so hoch gelobt wird und es mehr oder weniger ein Art Hype ist, ist es dennoch nur ein Werkzeug.

    Wie dieses Werkzeug eingesetzt wird und in welchem Umfang, muss IMHO jeder selbst entscheiden. Es ist nunmal so das die Konkurrenz groß ist und wir alle eigentlich nur ein Ziel haben. Eine Lösung für eine Anforderung zu liefern. Wie das im Enddefekt umgesetzt wird, interessiert den Kunden nicht.

    Man könnte auch die Frage stellen ob .NET alltagstauglicher ist als C++? Aber auch hier geht es nur um ein Werkzeug, welches eingesetzt wird und auch hier heißt es "it depends".

    Aus meiner Sicht, auch wenn mich nun alle Wissenvermittler und Technologieberater köpfen werden, ist ein komplett TDD getriebene Entwicklung in der heutigen Zeit nicht machbar. Warum? Der Kunde wird immer davon ausgehen das du als Entwickler das ganze getestet hast und es wird immer irgendwo ein Bug geben. Dem Kunden interessiert es nicht - er will seine Lösung und das schon gestern!
  3. GENiALi schrieb am Samstag, 17. Juli 2010 16:00:00 Uhr:

    Das sagt mein Chef auch immer. Der Kunde schaut nur auf den Preis. Mehr aufwand nur um etwas "richtig" zu machen zahlt der Kunde meistens nicht. Das ist ein dilemma.

    In dem grössenbreich wo ich Software etwickle spielt schlicht nur der Preis eine Rolle. Wenn man etwas offeriert, die Konkurenz ist bei 100K und wir bei 80K, dann ist klar wer gewinnt. Wir. Slebst wenn ein Auftraggeber eine öffentliche Institution ist.

    TDD uns so interessiert den Kunden schlicht nicht. Egal was der nutzen daraus sein könnte. Er sieht jetzt und hier nur die 80K.

    Die meisten Kunden wissen ja nicht mal was es heisst Software zu entwickeln. Sie können sich schlicht nicht vorstellen was solche Technicken für Vorteile haben können. Sie wünschen sich eine Software die einen Funktionsumfang hat. Wie der erreicht wird ist ihnen Egal. 80K sollen das selbe Endergebnis bringen wie 100K. Was soll er 20K mehr bezahlen? Das sieht er nicht ein.

    Ich bin auch nicht bereit in einem Fachgeschäft 1000 für ein TV zu bazahlen wenn ich es um die Ecke beim MM für 800 bekomme. Das selbe Gerät mit den selben Funktionen und zwangsläufig die selbe Qualität.

    Garantie habe ich bei beiden die selbe. Also, wie so mehr bezahlen?

    Vergleich hinkt einwenig. Aber die meisten Kunden sehen das so. Deshalb wird uns der Chef nie mehr Aufwand als nötig für die Entwicklung gewähren. Egal wie cool ist das finden täte.
  4. Carsti schrieb am Samstag, 17. Juli 2010 16:39:00 Uhr:

    Immer wenn mir jemand mit TDD kommt, antworte ich leicht sarkastisch: Ich entwickle nicht für den Tester, sondern für den Kunden. Soll nicht heißen, daß Code ohne Unittest und Codereading rausgeht, sondern daß man sich immer klar sein muß, für wen man arbeitet. Schnell pervertieren sich gut gemeinte Paradigmen zu einer Straße zur Hölle. Ich habe schon Fälle erlebt, da wurde ein Bug nicht ausgebaut, weil ja sonst die Tests umfallen würden. Bei sowas krieg ich Ausschlag...
  5. Gordon Breuer schrieb am Samstag, 17. Juli 2010 16:55:00 Uhr:

    Den ganzen Überlegungen ist kaum etwas hinzuzufügen. Noch extremer wird der Fall in Firmen mit mehreren Entwicklern, vermutlich mit unterschiedlichem Potential und Fähigkeiten. Diese müssen zunächst erstmal TDD erlernen. Der leidenschaftliche Programmierer, der sowas auch außerhalb seiner Arbeitszeit macht um einfach seinen individuellen "Marktwert" hoch zu halten ist dabei sicher nicht die Regel.

    @GENiALi
    Das Beispiel mit dem Fernseher hat denke ich den Nachteil, dass eben bei identischen Modellen nicht der eine weniger Qualitätstest hinter sich hat als der andere. Doch genau das ist der Fall bei der Softwareentwicklung, einmal mit und einmal ohne TDD.

    Wie man das Dilemma lösen kann? Keine Ahnung. Doch solange die Qualität hinter der Quantität zurücktreten muss werden Softwarefehler die sich mit TDD leicht hätten vermeiden lassen die Regel bleiben.
  6. Robert Mischke schrieb am Samstag, 17. Juli 2010 18:19:00 Uhr:

    Hallo, also ich entwickle seit 4, 5 Jahren testgetrieben und weiß gar nicht mehr wie das anders geht. Um ohne einen Test zu beginnen, müsste ich erst mal ordentlich nachdenken und wäre aus meinem Schema-F gerissen. Nicht mit Tests zu entwickeln würde mich, nach der Rechnung hier, so etwa 20% bis 30% langsamer machen. Tatsächlich wahrscheinlich mehr.

    Was ich allerdings nur in Einzelfällen Teste ist View Logik. Für die Repository Logik verwende ich Integrationstests, was auch das Beispiel erheblich verkürzen würde! Das ist mit dem Abfragen der einzelnen Felder ist auch super verbose: Objekt ins Repository rein, dann wieder raus und dann den Vergleich mit einem Asserter Objekt machen.

    Überhaupt ist das Unit-Testen von Infrastrukturcode sehr fragwürdig, dazu zählen für mich auch Repositories, Datamapping, etc..

    Ich würde immer auf das Testcoverage schielen, Werte von über 80% sind schon mit verhältnismäßig wenigen Integrationsstests zu erreichen, das schafft Sicherheit und gibt einem das Sicherheitsnetz für Veränderungen ohne Angst etwas kaputt zu machen, ohne das zu bemerken. Unit-Tests sollten immer nur eine Spielart sein. Zwischen Unit-Test und Integrationstest gibt es auch noch Programmer-Tests. Setzt man ein BDD Framework ein, können die verschieden Testebenen untereinander verschwimmen.

    Zum Thema Kundeinformation, Verkauf/Vertrieb undTestgetriebener Entwicklung: Das lässt sich sehr wohl als Pluspunkt verkaufen und ist auch gut greifbar. Den Kunden denen ich schon von Tests erzählt habe, hat eingeleuchtet, dass durch automatisierte Tests die Produktionskosten sinken.

  7. Werner M. schrieb am Sonntag, 18. Juli 2010 07:52:00 Uhr:

    Den Gedankengang kann ich sehr gut nachvollziehen. Das geht dann soweit dass ich klein und kleinst-Projekte, wenn diese meinen eigenen Qualitaetsanforderungen entsprechen sollen, um keinen realistischen (=fuer den Kunden bezahlbaren) Preis anbieten kann...
    Bei mir bleiben folgende Loesungsansaetze:

    Ich verwende (eigene) Application Frameworks deren qualitaetsanforderungen ich kenne, und welche diese im Einsatz auch bestaetigt haben. Die Logik des konkreten Projekts wird dann meist sehr klein, und wenn ich dann aus Kostengruenden den einen oder anderen Abstrich bei der Qualitaetssicherung machen muss tut es nicht so weh...

    In Wirklichkeit duerfte man diverse dieser Kleinstprojekte gar nicht machen, denn oftmals haben die Auftraggeber komplett falsche (unprofessionelle) Vorstellungen davon was (gute) Softwareentwicklung kostet, und wundern sich dann, wenn das Projekt nicht stabil läuft oder die Kosten am Ende doch wesentlich hoeher sind als erwartet.
    Wenn du aber um den höheren / realistischen Preis anbietest, dann bekommst du den Auftrag nicht, weil es gibt immer einen ders billiger macht....

    D.h. man wird eigentlich dazu gezwungen um einen unrealistischen Preis anzubieten in der Hoffnung im Laufe des Projektes irgendwo noch ein paar Euro rausschinden zu koennen. Traurig aber wahr

  8. Ralf Westphal schrieb am Sonntag, 18. Juli 2010 15:45:00 Uhr:

    Habe mir auch mal Gedanken zu der Ausgangsfrage gemacht. Sind aber länger geraten, deshalb findet ihr sie in meinem Blog: http://bit.ly/94niKl
  9. Jürgen Gutsch schrieb am Montag, 19. Juli 2010 09:15:00 Uhr:

    Hallo Thomas,

    das heißt, TDD ist für dich noch keine Selbstverständlichkeit. Mir geht es genauso, schließlich geht das nicht von Heut auf Morgen. Bei der entwicklung von Standardsoftware steht TD nicht zur Diskussion ist aus - den von Ralf - beschriebenen gründen als Pflicht zu sehen.
    Allerdings kann ich dein Problem das bei (kleinen) Kundenprojekten nachvollziehen. Die Frage ist: wie verkaufe, bzw. verrechne ich den plötzlichen Mehraufwand? Das auch hier TDD von Vorteil ist, steht außer Frage. Aber kanni ch das dem Kunden Verkaufen?

    Ich kann das nicht beantworten. Ich würde versuchen, dem Kunden TDD und Qualität nicht explizit zu verkaufen, schließlich setzt das der Kunde vorraus. Automatisierte Tests haben IMO auf der Rechnung nichts verloren. Lediglich eine Liste der erfolgreich gelaufenen Tests würde ich der Rechnung anhängen. Wenn TDD ein Teil der Entwicklungsarbeit ist, wird es einfach als Aufwand mit in die Entwicklung einbezogen.

    Bleibt immer noch die Frage ob der (Bestands-)Kunde den plötzlichen Mehraufwand zahlt...

    Viele Grüße aus dem Süden
    Jürgen
  10. Robert Mischke schrieb am Montag, 19. Juli 2010 10:23:00 Uhr:

    Es ist eine alte und nicht verschwindende Annahme, das testgetriebene Entwicklung aufwändiger ist als "klassische" Entwicklung. Das stimmt für kleine und große Projekte einfach nicht! Nicht einmal für Kleinstprojekte ist ein testgetriebener Ansatz zu teuer, vorausgesetzt man die Handgriffe dafür intus und macht Tests pragmatisch und kurz! (Das gegebene Codebeispiel ist das nicht!)

    Die Frage aus ökonomischer Sicht muss dann lauten: Wie hoch ist der Schulungs- oder Lernaufwand, bis man einen Entwickler soweit hat oder man selber soweit ist, das testgetriebene Entwicklung auch in kleinen Projekten effizient ist und Zeit spart. Dieser Lern und Schulungsaufwand wird sich bei kleinen Projekt sicher nicht unmittelbar bezahlt machen, doch die Investition lohnt sich, wenn man das auf Projekt der nächsten Jahre umschlägt. Nebenbei hebt man seinen Marktwert und zur Zeit lese ich überall, das Meisterschaft zufrieden macht - also die Investition macht gleich auch noch glücklicher.

    Kent Beck sagt auch noch etwas sehr spannendes zu tests: "Customer are not paying for tests, they paying for sotware that works. So whatever is the least amount of testing you can do, to give them software that works (...) that is the amount of testing you should do."

    Hörbar: http://www.cincomsmalltalk.com/blog/blogView?showComments=true&printTitle=Industry_Misinterpretations_164:_Going_for_the_Longball&entry=3436948975)


  11. Alex schrieb am Montag, 19. Juli 2010 10:52:00 Uhr:

    @Robert: Wie wären die Tests in o.g. Beispiel für Dich pragmatisch und kurz?
  12. Ralf Westphal schrieb am Montag, 19. Juli 2010 11:09:00 Uhr:

    @Jürgen und Thomas: Ich sehe es wie Robert.

    Tests müssen angemessen sein. Sie sind insofern eine Funktion des Projektes.

    Aber Tests müssen auch in der rechten Weise aufgesetzt sein. Sie sind insofern eine Funktion der Kompetenz des Entwicklers. Man muss halt auch testen können. (Womit ich nicht meine, dass man versteht, was [TextFixture] bedeutet. Testen ist eine Praktik, die man gut oder schlecht können kann. Man muss es halt lernen.)

    Wichtigste Voraussetzung, um an einer fundierten Diskussion für und wider Tests ist, würde ich sagen, die Testkompetenz. Die kann aber nur haben, wer sich echt mit Tests auseinandergesetzt hat. Unter einigen Wochen, die man sich darauf wirklich, wirklich eingelassen hat, ist die kaum gegeben.

    Dann erst sollte man sich erlauben darüber nachzudenken, ob und wie in einem bestimmten Projekt getestet werden sollte - wenn sich dann die Frage überhaupt noch stellt ;-)

    Schließlich noch: Den Kunden interessieren Tests nicht. Er setzt angemessene Maßnahmen voraus, die die Korrektheit seiner Software sicherstellen. Angemessenheit bedeutet, dass für die Korrektheit kein übermäßiger Aufwand getrieben wird. Das bedeutet nicht nur, dass automatisierte Tests auch mal unterlassen werden, sondern vor allem bedeutet das, dass automatisierte Tests überhaupt in Betracht gezogen werden. Kann der Entwickler sie überhaupt angemessen und in rechter Weise einsetzen? Das setzt der Kunde voraus - aber ich glaube, in vielen Fällen leider zu unrecht. Damit missbrauchen Projekte das Vertrauen ihres Kunden.

    Ob Teams Testkompetenz haben oder nicht ist jedoch in Bezug auf die Kommunikation einerlei. In keinem Fall muss man Tests dem Kunden verkaufen. Mir verkauft kein Arzt die Sterilisation seiner Instrumente. Und kein Buchhalter die doppelte Buchführung. Ärzte, Buchhalter, Bäcker usw. tun, was nötig ist, um einen professionellen Job zu machen.

    Dabei sind sie sich nicht zu schade zu sagen, dass etwas zu einem bestimmten Preis nicht geht. Ein 5-stöckiges Haus für 100.000 EUR - das würde ich mir auch wünschen. Aber das gibt es nicht, weil die Materialien und die statischen Berechnungen usw. es nicht hergeben. Da kann ich mich als Bauherr auf den Kopf stellen.

    Ich sehe bei der Diskussion um Tests also auch noch eine falsche Kommunikation, einen falschen Anspruch. Tests oder CI oder einen ORM verkaufe ich keinem Kunden, sondern arbeite einfach damit. Punkt.

    Man soll tun, was professionell in einem gegebenen Fall ist. Dafür muss man das aber erstens erkennen können. Und zweitens muss man es beherrschen können. Darüber reden, was professionell ist, muss ich mit dem Kunden aber nicht. Denn dafür kauft der mich ein als Entwickler. Ich soll das einfach wissen. Das Vertrauen setzt er in mich.
  13. Jürgen Gutsch schrieb am Montag, 19. Juli 2010 11:35:00 Uhr:

    Halo Ralf,

    ich sehe es eigentlich genauso. Aber leider ist test-getriebene Entwicklung gerade am Anfang aufwendiger als die klassische, herkömmliche Entwicklung. Es geht eine Weile, bis man die Best-Practices für sich, bzw. das Team gefunden hat. Es geht auch eine ganze Weile, bis man die Test-Frameworks beherscht. Du hast die Testkompetenz selber angesprochen. Wie ich oben beschrieben habe, amortisiert sich das bei Standardsoftware recht schnell, der Geschäftsleitung ist TDD in diesem Fall leicht zu verkaufen.

    Unternehmer wie Thomas, die auch kleine Kundenprojekte umsetzen, stehen gerade am Anfang aber vor einem Rechnungsproblem. Wie er schreibt, wird die Software später nicht weitergepflegt, der Lebenszyklus der Anwendung im Unternehmen ist sehr kurz und hört meist bei der Auslieferung auf. Eine Amortisierung innerhalb des Projektes ergibt sich also nicht, sondern nur längerfristig über mehrere Projekte hinweg. In diesem Fall muss das Unternehmen in der Anfangsphase enorm in Vorleistung gehen, da er den Mehraufwand nicht an den Kunden weitergeben kann.

    Siehst du das Problem in diesem Fall? Für Unternehmer ist es hier nicht einfach langfristig zu kalkulieren. Natürlich gibt es das Problem nicht mehr, sobald die Entwickler die nötige Testkompetenz erreicht haben. Aber bis dahin ist es ein schwerer und eventuell teurer weg.

    Viele Grüße
    Jürgen
  14. Werner Mairl schrieb am Montag, 19. Juli 2010 11:42:00 Uhr:

    "Tests müssen angemessen" stimmt!

    Trotzdem scheint es so zu sein als ob die Tests als erstes "dran glauben" müssen wenn es in der Kalkulation eng wird - das ist schade, wird man aber nur durch langfristiges umdenken lernen bzw. beheben können.

    Eine an das Thema angelehnte Frage in die illustre Runde:

    Ich schreibe ja einiges an Tests, aber in vielen Bereichen gäbe es noch einiges zu tun (ältere Projekte, besseres entkoppeln bevor man richtig testen kann usw)...
    Die praxis nähert sich halt langsam den Idealvorstellungen!

    Wenn ich jetzt aber über meine (recht erfolgreichen) Projekte der letzten 5-10 Jahre nachdenke, dann fällt mir auf, dass die Anzahl der explizit geschriebenen Tests "noch eher gering" ist um es mal vorsichtig zu umschreiben.
    Trotzdem habe ich es geschafft den Programmen ein hohes Maß an Stabilität beizubringen, und das wichtigste Werkzeug dabei war das "Assert".
    Zwar eine Eigenimplementierung aber in der Funktionsweise dem System.Diagnostics.Assert sehr ähnlich.
    Assert-Aufrufe finden sich bei mir im Code "zu tausenden", ganz grob geschätzt sage ich mal 1-2 Asserts pro 10 LOC.

    Mit Assert meine ich jetzt auch wirklich das Absichern von IMPLIZITEN Annahmen, explizite Annahmen (wie dass Parameter nicht NULL sein drüfen) werden weiterhin mit throw xxxException abgesichert.
    In manchen Projekten bin ich soweit gegangen die Asserts im ReleaseBuild sogar drinnen zu lassen (per Compilerschalter könnte man sie ja enntfernen)

    Mit dieser extrem-offensiv Strategie was Exceptions anbelangt (fehlgeschlagene Asserts werfen eine Exception), bin ich in den letzten 10 Jahren extrem erfolgreich, will heissen Anwendungen welche weltweit speziell USA und Pazific Raum im Einsatz sind machen keine großen Schwierigkeiten im Support.
    Sowohl bei den ganzen Beispielen im Web als auch den offengelgten Quellcodes von releasten Produkten finde ich diese technologie oftmals gar nicht eingesetzt!

    Etwas abstrakt würde ich eigentlich sagen, all meine Asserts sind doch auch eine Art "Tests" die unter anderem sicherstellen (versuchen) dass der Code innerhalb der vorgesehenen Parameter bleibt....

    Gibt es ähnliche/konträre Erfahrungen oder Ansichten dazu ?



  15. Ralf Westphal schrieb am Montag, 19. Juli 2010 12:06:00 Uhr:

    @Jürgen: Testkompetenz in Bezug auf automatisierte Tests gehört für mich zur Grundausbildung. Dort wird sie aber nicht geleistet. Also können viele Entwickler es nicht. Solange das der Fall ist, müssen wir uns darüber unterhalten. Wenn ich nicht schwimmen kann, sollte ich nicht über das Für und Wider von Schwimmstilen oder wann man Schwimmen sollte reden wollen.

    Auch einer Geschäftsleitung muss ich testgetriebene Entwicklung nicht verkaufen. Die ist mein Kunde als Softwareexperte. Wenn ich es für sinnvolle halte, autom. Tests einzusetzen, dann tue ich das. Punkt.

    Geschäftsleitungen können übrigens nur verkaufen, was man ihnen anbietet als Entwickler. Wenn ich sage, es dauert 10 Tage, dann dauert es 10 Tage. Ist halt so. Und wenn mir das die Geschäftsleitung nicht glaubt und mit mir darüber diskutieren will auf einem Level unterhalb funktionaler Anforderungen des Kunden, dann ist das ein grundsätzliches Problem. Ein Vertrauensproblem.

    Wenn die Geschäftsleitung ein Problem hat, 10 Tage dem Kunden zu verkaufen, dann ist das ein Problem unserer Branche. Falls ich die 10 Tage für angemessene Qualität brauche und irgendwelche "Cowboyprogrammierer" (oder der 15jährige Neffe des Chefs) es in 2 Tagen meinen hinzubekommen... weil sie auf Tests verzichten usw. Dann ist das auch ein grundsätzliches Problem. Nämlich eines der Ausbildung (oder Personalabteilungen), die es zulässt, dass Leute Entwickler sind, denen Kompetenzen fehlen.

    Und noch ein Aspekt: Der ganzen Diskussion unterliegt auch immer noch ein Ton, der sich nach Wasserfalldenke anhört. Hier sehe ich ein Wurzelproblem. Die Frage, ob Entwicklung mit vernünftigen Tests wirklich länger dauert oder nicht, hat ja vor allem Relevanz bei Festpreisprojekten, die einen fixen Scope in fixer Zeit mit fixer Quali versprechen. Wie wir aber inzw wissen, ist das für Software nur schwer zu erfüllen.

    Wenn hingegen in Timeboxes gedacht wird, die liefern, was man halt liefern kann... dann wird alles anders. Daher behaupte ich mal: Wer hier gegen den Aufwand mit autom. Tests wettert, der arbeitet in Wasserfallprojekten. Und das ist dann sein Wurzelproblem, an dem er arbeiten sollte.
  16. Jürgen Gutsch schrieb am Montag, 19. Juli 2010 12:43:00 Uhr:

    Hallo Ralf,

    du hast mich leider nicht verstanden und bist auch etwas am Thema vorbei :-(
    Möglicherweise hast du auch nie Situationen in kleinen Webentwicklungsfirmen erlebt, in denen die Geschäftsleitung auch die Entwicklung ist und in der die längsten Kundenprojekte 2 Wochen gehen.
    Es ging auch nicht darum Gut oder Schlecht zu diskutieren, sondern darum das Unternehmen so lange am leben zu erhalten, bis die nötige Testkompetenz vorhanden ist.

    Stell Dir einfach mal vor, so ein kleines Unternehmen, wie oben beschrieben, möchte TDD einführen, weil sie von dem Nutzen absolut überzeugt sind und damit die Qualität ihrer Projekte steigern können. Keiner hat ernsthaft TDD betrieben und sie stoßen auf folgendes: Das Team muss umdenken und die Bedienung der Testframeworks lernen, also Testkompetenz aneignen. Sie möchten das auf sich nehmen und investieren Zeit (= Aufwand = Geld) bis sie die Testkompetenz erreicht haben. In dieser Zeit machen sie aber weniger Umsatz und weniger Gewinn, da sie in der Lernphase weniger Projekte beenden.

    Ich sage an keiner Stelle, das dieses kleine Unternehmen nicht TDD einführen sollte. Sondern lediglich, dass es für die Geschäftsleitung eine ziemlich große Herausforderung ist. Und in dem Punkt wirst du mir ganz sicher zustimmen :-)

    Viele Grüße
    Jürgen
  17. Ralf Westphal schrieb am Montag, 19. Juli 2010 15:33:00 Uhr:

    @Jürgen: Es ist doch egal, ob das kleine Webteam mit "Chefentwickler" TDD lernen will oder Velocity oder NHibernate oder JQuery oder sonstwas. Lernen bedeutet Fehler machen. Lernen sollte also zunächst mal an Übungsprojekten stattfinden.

    Wenn es dafür keinen Raum gibt, dann ist das ein Wurzelproblem. Dann fährt die Firma auf blankem Metall. Ich empfehle die Lektüre von Tom DeMarcos "Spielräume". Die sind nicht nur wichtig fürs Lernen, sondern auch zum Ausgleichen von Fehlern und sonstige Veränderungen.

    Firmen, die glauben 100% der Zeit produktiv sein zu müssen (oder zu können), laufen auf den Burnout zu; den eigenen oder den ihrer Mitarbeiter.

    Wenn vor mir einer steht, der sagt, er wolle was gegen seine Kurzatmigkeit tun, dann rate ich ihm, mal Sport zu machen. Wenn sich dann aber herausstellt, dass er starker Raucher ist... dann nützt der ganze Sport kaum was. Dann muss er zuerst das Rauchen abstellen.

    Das wie die Einführung von Veränderungen jeder Art, ist eine Herausforderung für eine Geschäftsleitung. Aber genau da ist sie in ihrem Element. Das ist ihre Existenzberechtigung: Veränderung zu ermöglichen und Veränderungen zu begleiten. Wenn Geschäftsleitung daran scheitert, dann erfüllt sie ihren Auftrag nicht. Und nochmal: Das hat nichts mit TDD zu tun.

    (Nun wirst du sagen, dass die Geschäftsleitung doch verkaufen muss, sie muss das Geschäft am Leben halten. Wenn das so ist, dann ist es umso schwieriger. Entschuldigt ist sie deshalb jedoch nicht, sich um Evolvierbarkeit der Organisation zu bemühen. Sie macht sich nur das Leben selbst schwerer, je mehr Aufgaben sie übernehmen will: Geschäfte abschließen, Veränderungen anleiten, mit programmieren... Das widerspricht dem Single Responsibility Principle. Und das tut es auch, wenn die Firma noch klein ist und quasi nicht anders kann. Ich empfehle hier die Lektüre von "Kopf schlägt Kapital" von Prof. Faltin, der aufzeigt, wie man sich auch in kleinen Firmen auf das konzentrieren kann, was man am besten kann. "Gründen mit Komponenten" ist sein Thema.)
  18. Werner Mairl schrieb am Montag, 19. Juli 2010 16:07:00 Uhr:

    Testen gehört (für mich) zum Prozess der Software-Entwicklung dazu, egal nach welchem Modell (Agil,TDD, Wasserfall was auch immer)

    >Falls ich die 10 Tage für angemessene Qualität brauche und >irgendwelche "Cowboyprogrammierer" (oder der 15jährige Neffe des >Chefs) es in 2 Tagen meinen hinzubekommen...

    Genau da dürfte aber das Problem liegen.

    Ich erlebe es sehr häufig dass Kunden/Auftraggeber einfach keine Ahnung davon haben, wieviel seriöse Softwareentwicklung kostet.
    Wenn die Leute dann noch die Kosten einer Individualentwicklung mit dem Ladenpreis eines Produktes von der Stange vergleichen wirds besonders krass.

    Die eigentliche Frage lautet für mich also nicht wie "finanziere" ich TDD sondern wie komme ich zu vernünftigen Projekten bzw. wie gehe ich mit Kunden/Auftraggebern um welche realitäsferne Vorstellungen haben.

    Ich versuch mal auf die Autobranche um zu legen:

    Ein Kunde kommt zum Verkäufer und erklärt ihm dass er gerne ein Fahrzeug aus der 10.000-15.000 € Kategorie haben mächte also grad 5 Türer, 60 PS....

    Auf Nachfrage des Verkäufers erklärt der Kunde dass das Auto pro Jahr 50.000 km machen soll und normalerweise 4 Leute drinnen sitzen werden.

    Was mache ich mit so einem Kunden ?

    a) Verkaufen in der Hoffnung dass die nachträglichen Diskussionen und Streitereine schon irgendwie zu handhaben sind ?

    b) Versuchen ein größeres Modell zu verkaufen

    was ist wenn er auf b) nicht einsteigt ?

    c) zu Konkurenz schicken

    d) Beratung: versuchen klarzulegen warum das nicht funktionieren kann und eine andere Lösung finden - vielleicht kommt der Kunde ja drauf dass 3 der Leute auch anders Fahren können, und dann nur mehr 15.000km pro Jahr anstehen.... was schon wieder Sinn machen würde.


    Ich bevorzuge und bin langfristig am besten mit d) gefahren, aber immer gehts halt nicht.
    Es hat auch schon Fälle gegeben wo ich auf Option c) zurückgegriffen habe. Die Konkurrenz hat das Projekt dann mit Option a) umgesetzt.... wie solche Sachen ausgehen kann man sich denken.





  19. Jürgen Gutsch schrieb am Montag, 19. Juli 2010 16:57:00 Uhr:

    Hallo Ralf,

    das ist alles gut und richtig :-) Aber Deine Antwort sagt mir, dass Du wirklich nie mit einem solchen Betrieb in Berührung gekommen bist. Das unterstelle ich Dir jetzt mal einfach so :-) Leider sind das auch die Betriebe die unseren Nachwuchs ausbilden. Du und Stefan ihr wollt das Übel an der Wurzel packen: Dort ist es auch angesiedelt, genauso wie auch an den Berufsschulen und bei den Entwicklern selbst.
    Deine Empfehlung an die Geschäftsleitung, noch weiter umzudenken hat auch wieder mit Zeit und Aufwand zu tun. Ich bin sicher, dass sehr viele solcher Firmen auf blanken Metall fahren. Daher schreibe ich das ja. Für die ist es ein riesen Akt den Aufwand für die Umstellung bereit zu stellen. Das gilt wie Du sagst nicht nur für TDD. Leider geht die Initiative und Bereitschaft etwas zu verändern nicht immer vom einfachen Entwickler aus, das weist Du so gut wie ich. Sonst würde sich jeder Entwickler von sich aus weiter entwickeln und das oben genannte Problem hätten wir gar nicht. Du weist so gut wie ich, dass die Mehrheit der Entwickler um fünf nach Hause geht, um dann nichts mehr von Softwareentwicklung wissen zu wollen. Daher ist eine Umstellung für einige Firmen ein Aufwand, der wahrscheinlich nicht ohne Verluste zu schaffen ist.
    Ich möchte Dir ja auch eigentlich auch einfach nur sagen, dass alles immer leichter gesagt als getan ist ;-) Oder: It depends, auch wenn Du es bekanntlich nicht so gerne hörst ;-)

    Viele Grüße
    Jürgen
  20. Ralf Westphal schrieb am Montag, 19. Juli 2010 17:29:00 Uhr:

    @Jürgen: Ich habe 10 Jahre Branchensoftware entwickelt in einer kleinen Bude. Am Anfang waren wir 2, am Ende 5 - und dann bin ich ausgestiegen, weil ich keine Lust mehr auf die Problemdomäne hatte.

    Ich habe also sowohl Geschäft geführt wie selbst entwickelt wie Mitarbeiter angeleitet. Insofern hab ich schon ne Ahnung, worum es geht.

    Es hilft aber nix: Wer Veränderung nicht auf die Reihe kriegt, der stirbt. Und das hat nix mit TDD zu tun.

    Wer als Chef weiter blickt und sein Team nicht zur Veränderungen bewegen kann, der muss überlegen, woran das liegt? Die falschen Motivatoren? Die falschen Leute?

    Und wer als Entwickler sich verändern will und einen Chef hat, der das nicht will, der muss sich überlegen, ob es woanders nicht besser geht.

    "Jeder ist seines Glückes Schmied - aber nicht jeder Schmied hat Glück" wusste schon Harpe Kerkeling. Wenn es schwer fällt mit TDD und mit den Kunden sowieso, dann sind das nur Symptome für tieferliegendes. Darüber sollten wir dann nicht hier sprechen in einem Test-Thread.
  21. Karsten Samaschke schrieb am Montag, 19. Juli 2010 17:53:00 Uhr:

    Hach, ich finde es immer wieder amüsant, wie Theorie und Praxis, Wunschdenken und Realität aufeinander prallen. Ich persönlich kann beide Seiten verstehen, ich sitze auch auf beiden Stühlen. Als Trainer und Coach stehe ich auf dem Standpunkt, TDD als eines der effektivsten Mittel zur Fehlererkennung, -beseitigung und Qualitätsmaximierung anzusehen. Als Praktiker (Entwickler in diversen Projekten von "ganz klein" bis "ganz groß") kenne ich aber auch die tatsächlichen Probleme - Vorgehensweisen, die nun mal tatsächlich mehr Aufwand kosten, Ansätze, die erst erlernt und trainiert werden müssen, Kunden, die am Liebsten 100% Qualität zu 0% Kosten haben möchten.

    Ich persönlich sehe es eher pragmatisch: Test ja, aber nur, wo es sinnvoll ist. Mir nutzen 100% Testabdeckung überhaupt nix, wenn meine Tests (was ich dem einen oder anderen Poster mal unterstellen möchte) typische "Hmm, ja, luppt doch"-Tests sind, die also nur das Grundverhalten verifizieren und eigentlich kein einziges Problem (Grenzbereiche, logische Fehler, Sicherheit, etc.) abtesten. Weil, sowas würde Geld kosten, das macht man nicht in der selben Zeit, wie eine Funktionalität mal einfach hin zu rotzen. Diese typischen Trivial-Tests bringen nix, außer das Projektmanagement zufrieden zu stellen und sich selbst das Bändchen des Super-Duper-Testers umzuhängen. Andererseits kenne ich auch aus eigener Erfahrung Projekte, in denen man besser mal automatisierte Tests geschrieben hätte, vorsichtig ausgedrückt. Die entsprechenden Stellen lassen sich aber - in aller Regel - recht einfach identifizieren, denn es sind meist die komplexen Berechnungen, die eigentlichen Geschäftsregeln und weniger die billigen "Ich schreibe mal drei Datensätze in die Datenbank"-Geschichten. Und wer dort nicht lernt, dass Tests Leben retten können, hat noch nie ein entsprechendes Projekt versemmelt.

    Also, wie halte ich es abseits hochgestochener und ausufernder Darlegungen? Ganz einfach: Komplexe Funktionalitäten, die getestet werden müssen, werden intensiv getestet - und zwar negativ! Will heißen: Dass Code funktioniert, ist schnell bewiesen - er muss es auch in Problemsituationen und Grenzfällen tun. Ich setze mir also virtuell das Tester-Hütchen auf, ich überlege, wie ich die entsprechenden Funktionalitäten in Fehler reintreiben könnte, ich versuche, die entsprechenden Komponenten zu stressen. Das geschieht in den allermeisten Fällen in TDD-Manier, und meist auch ohne Bezahlung. Aber ohne diese Tests würde ich die Applikation niemals auch nur für einen Alpha-Test ausliefern. Bei den anderen Funktionalitäten ist es abhängig von deren Komplexität, meinem Vertrauen in den Code und die Frameworks und den Prioritäten. Und damit fahre ich ganz gut.

    Ich nenne diese Vorgehensweise "GMV"-Prinzip - "Gesunder Menschenverstand". Und genau das erzähle ich meinen Kunden und Seminarteilnehmern auch immer und immer wieder. Das Leben ist nämlich grau, nicht schwarz oder weiß.
  22. Ralf Westphal schrieb am Montag, 19. Juli 2010 18:37:00 Uhr:

    @Karsten: "Das geschieht in den allermeisten Fällen in TDD-Manier, und meist auch ohne Bezahlung."

    Wie kannst du Tests nicht bezahlt bekommen? Du bekommst Geld fürs Programmieren - nimmst dann aber Tests aus? Sind Tests kein Code? Programmierst du da nicht?

    Tests zu separieren und aus der Kalkulation raus zu lassen, halte ich für ein sehr, sehr kontraproduktives Verhalten für die gesamte Branche. Solches Denken führt dazu, dass es zweierlei Maß gibt: einen Preis mit Tests und einen Preis ohne Tests.

    Und das führt dazu, dass Tests weiterhin als Option für Warmduscher angesehen werden. "Der da drüben kann das ohne. Was bist du denn für ein Programmierer, dass du autom. testen willst."

    Es ist doch auch eine Milchmädchenrechnung: Getestet wird immer. Einer muss es tun. Entweder der Entwickler. Oder die Testabteilung. Oder der Kunde.

    Die Frage ist nur, bei wem ein aufpoppender Fehler am meisten Probleme macht.
  23. Karsten Samaschke schrieb am Montag, 19. Juli 2010 19:09:00 Uhr:

    @Ralf: Meine Aussage ist, dass ich diese Tests in jedem Fall mache. Ich stelle sie nicht explizit als einzelne Posten in Rechnung, sondern sie sind Bestandteil des Gesamtpakets, werden von Anfang an eingepreist und kommuniziert. Sie sind für mich selbstverständlicher Bestandteil meiner täglichen Arbeit, aber das wird Dir sicherlich schon klar gewesen sein.
  24. Ralf Westphal schrieb am Montag, 19. Juli 2010 19:19:00 Uhr:

    @Karsten: Ne, das war mir nicht klar. Aber ich bin beruhigt. Auch darüber, dass du Tests nicht separat in Rechnung stellst. Puh... nochmal alles gut gegangen ;-)
  25. Karsten Samaschke schrieb am Montag, 19. Juli 2010 19:30:00 Uhr:

    @Ralf Na, dann bin ich ja froh. :-)
  26. Sebastian schrieb am Montag, 19. Juli 2010 22:54:00 Uhr:

    Nach dem ich mir nun alle Beiträge durchgelesen habe sind zwei Sachen für mich klar:
    a) Zu testen finden alle gut
    b) nicht alle testen alles.

    Ich selbst kann Jürgen nur zustimmen. Kleine Unternehmen tun sich schwer darin TDD zu betreiben. Ich sehe es bei uns selbst. Auf der anderen Seite kann ich die Aussage "Wer Veränderung nicht auf die Reihe kriegt, der stirbt" von Ralf auch unterschreiben. Ich gehe daher den Mittelweg, d.h. ich teste nur die Kernkomponenten in kleineren Projekten. Wohingegen bei größeren Projekten ganz auf TDD gesetzt wird.

    Letztlich stellt sich aber einfach immer die Frage, was taugen die Tests? Gerade wenn der Entwickler auch der Testschreiber ist, ist die Frage für mich berechtigt.
  27. Thomas schrieb am Montag, 19. Juli 2010 23:36:00 Uhr:

    @Ralf:

    Der Diskussion liegen zwei unterschiedliche Business Cases zugrunde.

    Du musst als Prophet deine eigene Religion verkaufen, die du zum Teil auch selbst geschaffen hast und von der du als Consultant lebst. Du bezeichnest dich ja zudem sicher nicht umsonst als "Thinktank". Okay so, liefert viele gute Ideen und Ansätze und kreiert Diskussionen in der Branche und sorgt für Fortschritt. Dafür gebührt dir Anerkennung und Respekt.

    Mein Business Case ist die Entwicklung und der Verkauf (Multi Responsibility Principle :-P) von Software/Websites/Webanwendungen. Um mich von der Konkurrenz abzugrenzen und auch aus Eigeninteresse (Spaß + Marktwert) versuche ich ständig die Qualität meiner Lösungen zu verbessern bzw. selbst gesteckte Ziele und Standards einzuhalten.

    Dafür braucht es natürlich auch Prinzipien und Prinzipientreue. Aber vor allem braucht es neben den passenden Werkzeugen den gesunden Menschenverstand. Wie auch bei anderen Religionen ;) gilt: wer alles wörtlich auslegt, hat einen an der Klatsche.

    Bei vielen Live-Diskussionen rund um Clean Code mit Stefan habe ich die Stelle erlebt, in der es dann bei frustrierten Entwicklern ob ihres Kampfes gegen Windmühlen hieß: "Ja, sorry. Dann musst du den Arbeitgeber wechseln." Für einen Angestellten mag das stimmen. Für mich als Unternehmer und kleiner Fisch im großen Haifischbecken wäre das mehr als töricht. Ich kann mir meine Kunden nur zu einem gewissen Grad aussuchen - je höher das Ross auf das ich mich setze (ok, etwas doof als Fisch, zugegeben), desto tiefer der obligatorische Fall bei dieser Einstellung.

    Das heißt nicht, dass ich mit dem Schwager des Kunden, der "auch sowas gelernt hat, an der VHS", konkurrieren oder ein Angebot für die geplante Mondrakete zum Preis des Kleinwagens abgeben muss. Nein, ich kann den Wert meiner Arbeit schon ganz gut einschätzen und lehne auch viele Projekte schlicht und einfach ab.

    Aber kommen wir zum Punkt Reflexion über das Tooling. TDD ist ein Tool. Es heißt: Wer nur einen Hammer hat, sieht in jedem Problem einen Nagel.

    So ähnlich kam ich mir am Samstag vor, der Hammer war TDD. Ich war so dermaßen auf dem Trip und gefangen von dem Gedanken möglichst nur noch test-getrieben zu entwickeln, dass ich die Konsequenzen aus den Augen verloren habe.

    Aufgefallen ist mir das, als ich vor zwei Wochen begonnen habe, eine zwei Jahre alte Komponente um einige Funktionen zu erweitern (Ein Onlineshop-Modul für unser CMS). Alles aus der Pre-TDD-Zeit, auch die Erweiterungen habe ich nicht test-getrieben implementiert. Das Ergebnis war: eine gut funktionierende Software, funktionierendes Refactoring, in kurzer Zeit gut verdientes Geld, ein sehr zufriedener Kunde.

    Das heißt die Lebenswirklichkeit ist nicht schwarz oder weiß, sondern sie ist bunt. Der Verzicht auf TDD bedeutet nicht automatisch die Entstehung von unwartbaren Softwareklumpen - oder auch früher war nicht alles schlecht. Stimmt wirklich.

    So, nun hatte ich diesen Job in erstaunlich kurzer Zeit abgeschlossen, die Rechnung war gedruckt, als ich mich an ein laufendes, internes, Projekt gesetzt habe, bei dem ich selbst der Kunde bin. Das Projekt besteht aus so dermaßen losgekoppelten Komponenten, dass ich teilweise tagelang keinen Browser beim Entwickeln geöffnet habe (es ist ein Webprojekt, klar). In mehr als 120 Stunden habe ich nun die Grundfunktionalität und Infrastruktur geschaffen, mehr als 500 Unit- und Integrationstests geschrieben, Controller, Services und Repositories sind alle getestet. Als Entwickler erfüllt mich der Start des Testrunners mit Zufriedenheit, auch wenn das Anpassen aller Tests bei der Umstellung von Guids auf Integer neulich eine Qual war ...

    Als Kunde oder Projektmanager hingegen kotzt mich das Ganze an. Es ist wie gesagt ein internes Projekt und eigentlich sollte es schon längst live sein. Es kräht kein Hahn danach ob nun automatisiert getestet wird, ob die Controller-Action X auch ja das Attribut Y verpasst bekommen hat. Am Ende zählt sowieso nur das, was der User sieht.

    Aber der sieht gar nichts, weil ich nur damit beschäftigt war eine tolle Software zu bauen, dabei aber einfach viel zu lange gebraucht habe. Dabei ist das nicht mein erstes Projekt, in dem ich TDD einsetze, ich bin nun also nicht ganz grün hinter den Ohren, wenn auch sicher nicht perfekt. Aber ich bin über den Punkt hinweg, an dem ich es jemandem abnehmen würde, der behauptet, TDD bedeute keinen Mehraufwand (an Zeit).

    Reflektieren wir. Ich sehe zwei grundsätzliche Möglichkeiten:

    a) Man rotzt einen Prototypen ohne (automatisierte) Tests raus. Wenn das Ding gut funktioniert, wird (nach und nach) alles neu geschrieben.
    b) Man priorisiert die automatisiert zu testenden Bereiche und konzentriert sich auf diese, in denen der Teufel wirklich im Detail steckt. Die dadurch gewonnene Zeit steckt man in die nicht test-getriebene Entwicklung von simpler Funktionaltität, aus der Webanwendungen sowieso zu 80% bestehen.

    Die erste Möglichkeit widerstrebt meinem persönlichen Verlangen, robuste und gute Software zu erstellen. Und sie widerspricht der Lebenswirklichkeit: wenn das Ding wirklich läuft, gibt es ganz andere Baustellen - dann wird eher Funktionalität erweitert bis es nicht mehr geht und alles für viel zu viel Geld umgeworfen und neu gemacht werden muss.

    Die zweite Möglichkeit ist das, was sich für mich langsam als Ergebnis all der Überlegungen herauskristallisiert - ein Punkt, an dem ich schon im April einmal war:

    http://blog.thomasbandt.de/39/2330/de/blog/blog/blog/repositories-testen.html

    "Your time is finite; spend it more effectively elsewhere."

    View- und Controller-Logik? Nicht primär (automatisiert!) testenswert.
    CRUD-Funktionalität in Services und Repositories? Nicht testenswert.
    Validierungslogik? Testenswert.
    Komplexere Funktionalität, z.B. Parser? Testenswert!

    Unterm Strich also: test-first nur noch für Teile, die über 0815-Standardfunktionen hinausgehen. Views, Controller, Mapping in Repositories usw. sind alles Dinge, die leicht überschaubar sind und in denen sich Fehler auch schnell finden und beheben lassen.

    Ich glaube das ergibt einen guten Kompromiss nach gesundem Menschenverstand. Es beseitigt die latent existierenden Zweifel ob der Vorgehensweise, es beschleunigt den Projektfortschritt und es bringt den Spaß zurück - sowohl an der Implementierung der ganzen simplen Sachen, die schneller ohne Tests von der Hand gehen, als auch an der test-getriebenen Implementierung der komplexeren Teile. Dabei glaube ich auch nicht dass die Qualtität leidet - dadurch dass relevante Teile weiterhin test-first entwickelt werden, bleiben auch die Seiteneffekte der Tests auf die Architektur (lose Koppelung) erhalten. Und wenn zwei oder drei Bugs mehr zur Laufzeit im Browser auftauchen? Geschenkt.
  28. Jürgen Gutsch schrieb am Dienstag, 20. Juli 2010 07:49:00 Uhr:

    @Thomas, @Karsten, Danke, das ist genau das, was ich ausdrücken wollte. :-)

    (Als "Nicht-Studierter" fällt es mir teilweise etwas schwer die richtigen Begriffe und Wörter zu finden und mich gegenüber wortgewanteren Menschen verständlich auszudrücken.)

    @Ralf, Thomas Welt ist Bund, Karstens ist Grau (was wohl an Berlin liegen mag). Schwarz oder weis habe ich bisher auch nicht kennen gelernt. Ich habe mehrere Jahre als Entwickler in einer Firma gearbeitet, die sich bewust war dass sie noch einiges für eine bessere Qualität tun konnte, es aber aus Mangel an Resourcen einfach nicht hinbekommen kann, bzw. nur sehr langsam. Übrigens gibt es das Unternehmen seit 1997 heute noch.
    Je größer das Unternehmen oder das Team, desto einfacher ist es eine Person die Vorarbeit machen zu lassen. Ich wollte nur sagen, dass kleine Unternehmen große Schwierigkeiten haben Umstellungen zu verkraften. Selbstverständlich liegt ein grundlegenderes Problem dahinter, aber wenn dieses Lösbar wären, hätten die es längst gelöst. Schließlich sind die wenigsten Geschäftsführer blind und blöd ;-)
    Im jetzigen Team "predige" ich den anderen extremen Weg: grundsätzlich Test Driven, ganz egal ob beim Refactoring oder beim Neuentwickeln. Code wird erst angefasst, wenn es einen Test dazu gibt. Der Grund hier ist aber nicht nur, vorher den Code zu testen und funktionalität sicher zu stellen, sondern hauptsächlich auch um sicher zu gehen, dass sich die Entwickler nicht gegenseitig ins Handwerk pfuschen (à la "viele Köche verderben den Brei"). Hier geht es jetzt drum die höchst mögliche Testabdeckung zu bekommen um den Entwicklern und der Geschäftsleitung ein Gefühl der Sicherheit zu vermitteln.

    @Ralf, komm doch zur See# Party (ich hätte auch noch eine Idee für die Abendverasntaltung, kommt per Mail) dann können wir dort weiter diskutieren :-)

    Viele Grüße
    Jürgen
  29. Alex schrieb am Dienstag, 20. Juli 2010 08:33:00 Uhr:

    @Jürgen: d.h. Ihr testet auch Dinge wie Mappings, View- und Controllerlogik?

    Ich bin im Moment noch skeptisch, ob es gut ist, nicht alles zu testen.

    Eröffnet sich damit nicht wieder der Punkt, wo gefragt werden muss, was denn nun wirklich testenswert ist an der App?

    Für jemand, der täglich mit z.B. Integralfunktionen arbeitet, erscheint
    das nicht testenswert, weil er es aus dem "ff" kann.

    Ein anderer würde vielleicht bereits einfachere mathematische Zusammenhänge durch Tests absichern.

    D.h. wir haben hier wieder das klassische "it depends" erreicht, womit gerade Einsteigern natürlich überhaupt nicht geholfen ist.
  30. Thomas schrieb am Dienstag, 20. Juli 2010 09:17:00 Uhr:

    @Alex:

    "Eröffnet sich damit nicht wieder der Punkt, wo gefragt werden muss, was denn nun wirklich testenswert ist an der App? "

    Genau darum geht's hier - mir zumindest. Fragst du Steve Sanderson, oder auch mich jetzt, ob Controller-Logik testenswert ist, bekommst du ein "eher nein". Hört oder liest das einer der Fundamentalisten, gibt's ne Diskussion .-). Und fragst du 2 Leute, wirst du 3 Antworten bekommen ...

    Die Lösung wäre halt einfach im Zweifel immer für den Test zu entscheiden, sicher ist sicher. Nur kollidiert das, wie bereits geschrieben, mit der realen Welt da draußen ...
  31. Jürgen Gutsch schrieb am Dienstag, 20. Juli 2010 09:24:00 Uhr:

    @Alex, ja, hier sollte alles getestet werden, was jetzt nicht einfach einen Aufruf durchreicht. Einfach deswegen, weil sich die Entwickler beim Refactoring teilweise sehr unsicher sind und Angst vor Nebeneffekten haben, die in der Vergangenheit oft aufgetreten sind. Beim Mapping habe ich auch schon Fehler gesehen die beim casten aufgetreten sind. Nicht alle Tests sind klassische Unit Tests, sondern viele auch Funktionsabläufe testen und die zusammenarbeit der Units sicher stellen.

    Zu steife, umständliche Regeln helfen Einsteigern auch nicht und können - im Gegenteil - alles verkomplizieren. Unit-Testing sollte nicht zu einer Wissenschaft aufgebauscht werden, sondern selbstverständlich werden. Natürlich kommt es immer auf den Anwendungsfall an. "It depends" ist eine legitime Regel, schließlich ist kein Team, kein Unternehmen und kein Projekt gleich. Den Einsteigern ist lediglich klar zu machen auf was es letztendlich ankommt, bzw. auf was zu achten ist.

    Eventuell fahre ich hier im neuen Unternehmen auch falsch mit meiner strikten Regelung, aber das wird sich zeigen und im Laufe der Zeit von selber einpendeln.

    @Ralf, Nachtrag: Ich bin Clean Code Developer, weil ich von der Richtigkeit der Prinzipien und Praktiken überzeugt bin. Aber ich bin nicht in der Lage ein Unternehmen so umzukrempeln dass das Team alles zu 100% umsetzen kann. Ich würde es eventuell auch nicht wollen. Ich werde im Gegenteil die Prinzipien und Praktiken so einsetzen, wie es das Unternehmen und das Team zulässt. Ich werde CCD an das Unternehmen und das Team anpassen und nicht umgekehrt. Das gilt für TDD und alles andere. Ich sehe eben den Sinn in der Initiative in erster Linie darin, die Prinzipien und Praktiken zu vermitteln. Wenn das nicht der Sinn der CCD-Initiative ist, kannst du mich gerne aus der Liste herausnehmen :-)

    Viele Grüße
    Jürgen
  32. Thomas Höhler schrieb am Dienstag, 20. Juli 2010 09:26:00 Uhr:

    @Alex: Ich gebe Dir Recht wenn Du sagst dass ein "it depends" für Einsteiger nicht hilfreich ist. Ich glaube dass Einsteiger gerade mit den kleinen, einfach Test anfangen sollten um einfach ein Gefühl für die Tests zu bekommen und sich dann immer mehr an die komplexen Test rantrauen. Aber auf dem Weg vom Noob zum Pro merkt man auch dass viele Dinge enorm viel Zeit kosten und evtl in keinem Verhältnis mehr stehen.

    Es ist aber die Frage welche Zielgruppe Thomas hier anspricht. Ich denke mit seinem Post hat er nur die (für mich) natürliche Entwicklung in seinem Erkenntnispfad beschrieben und sich von einem starren Paradigma (dank eigener Reflektion) gelöst.

    Dann ist noch die Frage, ob man Integralfunktionen nicht doch testet obwohl man sie aus dem FF kennt. Hier kommt es dann auch auf die Wartbarkeit durch Dritte an, die sich evtl. nicht so gut auskennen mit Integralfunktionen. Hier bringen Tests auch dem Nachfolgenden Vorteile. Man kann und soll diese Vorteile dann nicht ausser acht lassen. Dinge, die aber zur grundsätzlichen Technologie gehören müssen m.E. nicht integriert werden.

    Als Grundsatz kann man sagen, dass ein Dritter mit ausreichenden technischem Wissen, der das Projekt nicht kennt, durch Dokumentation und Tests innerhalb kürzester Zeit in die Lage versetzt werden muss das Projekt verstehen und warten zu können.
  33. Karsten Samaschke schrieb am Dienstag, 20. Juli 2010 09:29:00 Uhr:

    @Alex Auch als jemand, der tagein und tagaus mit Integralfunktionen arbeitet, solltest Du wissen, an welchen Stellen die Dinger bösartig werden, wo und wie man sie ins Trudeln bringt, was komplexere Vorgänge sind, etc. Genau das ist ja der falsche Gedanke: "Ich beherrsche es, also teste ich es nicht" - der richtige Gedanke ist: "Ich beherrsche es (Grundfunktionalität luppt, mit einem oder zwei Tests hinterlegt), jetzt teste ich bösartig (wie kann ich es in Probleme bringen, wo läuft es gegen die Wand, was würde meine Oma eingeben)".

    Das Testing (und gerade das bösartige Testing) muss in jedem Fall passieren, denn sonst sind wir wieder bei den Alibi-Tests, die eigentlich nix bringen. Als jemand, der Integralfunktionen aus dem FF kennt, hast Du beim Schreiben der Tests Vorteile - eben weil Du schon weißt, wo die Problemfälle liegen. Das hat aber auf das eigentliche Testing keine Auswirkungen, sonst hast Du etwas falsch gemacht. Da gibt es normalerweise auch keine ausgedehnten Grau- oder Buntzonen, die kritischen Bereiche und die sicherheitsrelevanten Bereiche kann man durchaus identifizieren und dann auch testen. Mir kommt es dabei stets darauf an, die Bösartigkeit mit ins Spiel zu bringen, die will ich hier haben.

    Ein "it depends" gibt es eigentlich nur insofern, als dass es eben Dinge gibt, die nicht unbedingt getestet werden müssen. Unzweifelhaft ist es bei geschäftlichen Logiken (Validierung, Business-Logik, etc.), zweifelhafter wird es bei Infrastruktur-Code und sehr zweifelhaft dann beim Markup (das lässt sich meist ohnehin nicht sinnvoll automatisiert abtesten, jedenfalls nicht mit Unit-Tests).

    Noch ein Gedanke: Wenn ich dafür plädiere, gezielter Tests zu schreiben, dann handelt es sich dabei um ein Plädoyer, das sich an gestandene Entwickler mit Erfahrung im TDD richtet. Eher unerfahreneren oder unsicheren Entwickler würde ich raten, lieber zu viele Tests zu schreiben und auch vielleicht unsinnigere Dinge abzutesten. Es kommt dann sehr schnell der Punkt, an dem man erkennen kann, was sinnvoll zu testen war und was nicht. Man muss halt drüber reflektieren, aber man kann es dann nach kürzerer Zeit schon recht sicher erkennen.
  34. Thomas schrieb am Dienstag, 20. Juli 2010 09:34:00 Uhr:

    @Thomas #32

    "Ich glaube dass Einsteiger gerade mit den kleinen, einfach Test anfangen sollten um einfach ein Gefühl für die Tests zu bekommen und sich dann immer mehr an die komplexen Test rantrauen. "

    Nee! Unit Tests sind immer einfach, sonst macht man was falsch. Zwar kann man sich mit den bekannten Mocking Frameworks graue Haare züchten, aber einfache oder komplexe Unittests sehe ich nicht. Und an ConsoleApplication13 lernt man nicht, wie man richtig testet. Hier muss man schon, wie auch Ralf glaube ich irgendwo oben schon schrieb, einfach mal über einen langen Zeitraum, am besten mehrere Wochen/Monate und/oder Projekte, Erfahrungen machen.

    So wie Karsten es in #33 schreibt:

    "Eher unerfahreneren oder unsicheren Entwickler würde ich raten, lieber zu viele Tests zu schreiben und auch vielleicht unsinnigere Dinge abzutesten. Es kommt dann sehr schnell der Punkt, an dem man erkennen kann, was sinnvoll zu testen war und was nicht. Man muss halt drüber reflektieren, aber man kann es dann nach kürzerer Zeit schon recht sicher erkennen. "
  35. Thomas Höhler schrieb am Dienstag, 20. Juli 2010 09:55:00 Uhr:

    @Thomas: Mehr oder weniger dass was ich ausdrücken wollte. Ich denke dass Einsteiger versuchen sollten zu einer nahzu 100%igen Testabdeckung zu kommen um in die Lage zu kommen selbst entscheiden zu können was sinnvoll ist oder nicht.

    Mit einfachen Test meine ich eben auch die grauen Haare. Mit komplexem Mocking muss man nicht gerade anfangen wenn man mit TDD beginnt. Die Testcases an sich sollten einfach sein, der Weg dahin kann durchaus steinig sein. Deshalb immer damit anfangen was man überblicken kann und sich schrittweise an die komplizierteren Dinge mit den entsprechenden Tools ranwagen.
  36. Christina schrieb am Dienstag, 20. Juli 2010 22:21:00 Uhr:

    Hallo,

    ich kann nicht aus der Sicht des Unternehmers sprechen, nur als jemand, der zusammen mit einem sehr buntem Team an dem Unternehmensoftware - also keinen Kundensoftware - arbeitet.

    Erstens: ich finde das GMV-Prinzip IMMER gut. Ohne das würden wir nichts wirklich fertigstellen.

    Zweitens: in der letzten Zeit codiere ich einfach aus Faulheit nach TDD: es ist viel einfacher, eine Funktionalität durch einen Test aufzurufen, als eine Webseite, Konsolenanwendung oder sonstwas dazu zu bauen. Und dadurch entsteht ja auch das berühmte "Nebeneffekt", die Tests dazu zu gewinnen;)

    Drittens: der zweite Grund für mein Beharren auf Tests ist derselbe, den @Jürgen in #28 geschrieben hat: viele Entwickler -> größere Fehlerquoten. Ich tue dadurch mein Bestes dafür, um diese Fehler aus zu schließen.

    Viertens: mein ganz persönlicher Ehrgeiz: "fool me once, shame on you. Fool me twice...?!" Obwohl es hier um einen nachträglichen Test geht, finde ich trotzdem, dass man diesen Aspekt zu dieser wunderbaren Diskussion hinzufügen sollte.

    Schöne Grüße,
    Christina
  37. Golo Roden schrieb am Dienstag, 3. August 2010 10:23:00 Uhr:

    Hi Thomas,

    Du schreibst:

    "Aufgefallen ist mir das, als ich vor zwei Wochen begonnen habe, eine zwei Jahre alte Komponente um einige Funktionen zu erweitern (Ein Onlineshop-Modul für unser CMS). Alles aus der Pre-TDD-Zeit, auch die Erweiterungen habe ich nicht test-getrieben implementiert. Das Ergebnis war: eine gut funktionierende Software, funktionierendes Refactoring, in kurzer Zeit gut verdientes Geld, ein sehr zufriedener Kunde."

    Woher weißt Du, dass es funktioniert? Vermutlich hast Du das nicht ausgewürfelt, sondern es überprüft - irgendwie. Per nachträglich geschriebenem Unittest, per Hand, per externem Tester, per was weiß ich was.

    Der Punkt ist aber: Testen musst Du es so oder so - und wenn Dein Test nur aus "Ich klick schnell mal durch" besteht. Du hast aber den Aufwand. Ansonsten lieferst Du etwas aus, von dem Du nur im guten Glauben bist, dass es gut funktioniert.

    Niemand hat behauptet, dass man nicht auch ohne TDD gute Software schreiben könne. Mit TDD erspart man sich aber viel Arbeit. Denn wie Du schon selbst geschrieben hast: TDD hat seinen Charme. Und der Preis dafür ist, die Tests nicht immer und immer wieder per Hand durchführen zu müssen, sondern von vornherein (!) die Gewissheit zu haben, dass das, was man macht, getestet ist.

    Und da in der Informatik gilt, dass ein Fehler desto teurer ist, je später man ihn behebt, spart TDD unterm Strich Geld.

    Just my 2 cents,


    Golo


    PS: "Wie viel kosten Unittests?", siehe http://www.des-eisbaeren-blog.de/post.aspx?id=1cb31202-72a0-48bc-b433-4488c8b5c873
  38. Frank schrieb am Dienstag, 10. August 2010 20:23:00 Uhr:

    Hallo, ich übe mich jetzt seit wenigen Wochen in tdd und unittest :) und mir ist da folgende Frage gekommen. wenn ich eine Komponente nachträglich MEF fähig mache, sprich Imports und Exports definiere, sollte ich diese Attribute dann auch in UnitTest testen? Irgendwie sagt mir mein Gefühl ja. wie seht ihr das?
  39. Rene Drescher-Hackel schrieb am Montag, 16. August 2010 23:16:00 Uhr:

    Interessante Diskussion...

    @Ralf(17):
    "Firmen, die glauben 100% der Zeit produktiv sein zu müssen (oder zu können), laufen auf den Burnout zu; den eigenen oder den ihrer Mitarbeiter."

    wohl eher das der Mitarbeiter ;-) - die sind im Fall der Fälle austauschbar.

    @Jürgen(16):

    "Es ging auch nicht darum Gut oder Schlecht zu diskutieren, sondern darum das Unternehmen so lange am leben zu erhalten, bis die nötige Testkompetenz vorhanden ist."

    würdest du - nur des Preises wegen - bei solch einer Firma ein Auto kaufen? Die Firma muss Kompetenzen schaffen und pflegen. Ich habe es immer wieder in Firmen erlebt, wo Mitarbeiter einfach nicht den Antrieb haben, außerhalb der bezahlten Arbeitszeit, sich weiterzubilden - die Einführung von TDD in den Entwicklungsprozess der Firma gehört für mich zu den Aufgaben der individuellen Weiterbildung. Je schneller und besser Mitarbeiter die Kompetenz erlangen und untereinander teilen - desto mehr gewinnt der Mitarbeiter (an Vertrauen) und die Firma (an Kunden). In (19) hast du es dann ja auch selbst geschrieben - es ist bittere Realität.

    @Karsten(21):
    "das macht man nicht in der selben Zeit, wie eine Funktionalität mal einfach hin zu rotzen. Diese typischen Trivial-Tests bringen nix, außer das Projektmanagement zufrieden zu stellen und sich selbst das Bändchen des Super-Duper-Testers umzuhängen"

    dem ist nichts hinzuzufügen - leider ist es so

    @Jürgen(31):
    "Aber ich bin nicht in der Lage ein Unternehmen so umzukrempeln dass das Team alles zu 100% umsetzen kann. Ich würde es eventuell auch nicht wollen. Ich werde im Gegenteil die Prinzipien und Praktiken so einsetzen, wie es das Unternehmen und das Team zulässt. Ich werde CCD an das Unternehmen und das Team anpassen und nicht umgekehrt. Das gilt für TDD und alles andere"

    alles andere wäre gelinde gesagt "Selbstmord".

    Also getreu dem Motto: "Es gibt nichts Gutes - außer man tut es!"
    Ich persönlich halte es letztlich wir Jürgen - ich versuche es durch meine Arbeit zu vermitteln, meinen Projektleiter, meinen Kollegen zu überzeugen und letztlich mitzureißen. TDD oder CCD mit der Brechstange geht gleichermaßen nach hinten los. Ich halte es auch nicht für besonders sinnvoll eine if-Abfrage

    if(a == 1){ return true; }

    in eine Methode IsAGleich1(short a)

    auszulagern. Das stellen sich bei mir die Nackenhaare auf! Und genau so verhält es sich mit dem TDD. Ich hatte nach wie vor noch nicht die Notwendigkeit/das Bedürfnis Tests zu schreiben oder testgetrieben zu entwickeln (gleich ich es mir hin und wieder gut vorstellen könnte). Heute habe ich mich mehr oder weniger dabei ertappt, dass ich wieder Code in Frage gestellt habe. Die Komponente tut ihren Dienst (nach dem refaktorieren), weil mir letztlich klar war, was ich tue - denke ich mal.

    Aber wie Ralf es schon sagte, vieles ist ein Grundproblem im allgemeinen Verständnis, liegt in der Ausbildung, etc.

    Gruß
    Rene


  40. Florian Fanderl schrieb am Dienstag, 31. August 2010 13:29:00 Uhr:

    Hallo zusammen,

    Ich fange gerade auch an in TDD zu programmieren. Bei uns gab es vorher gar keine Unit Tests, deswegen muss ich mich da auch erstmal hinein finden.
    Aber was ich eigentlich sagen wollte: Ich denke gar nicht dass TDD soooo viel mehr Aufwand ist. Wenn ich etwas programmiere, dann überprüfe ich doch sowieso in einem gewissen Maß ob das was ich programmiert habe funktioniert. Entweder ich schaue ob im Client das ankommt was ich haben will oder im schlechtesten Fall klick ich mich durchs debuggen.
    Diese Zeit habe ich im Endeffekt auch verloren. Ich kann momentan nur vermuten, aber ich denke der gefühlte zeitlich Mehraufwand zum Testen ist wahrscheinlich höher als der beim manuellen Testen. Netto denke ich nicht sehr viel um UND man kann die Tests später automatisiert wiederholen.
    Die Frage die sich natürlich wieder stellt ist, wie feingranular man TDD ansetzt. Es gibt ja durchaus unterschiedliche Levels auf denen man testen kann.
  41. Mi chael Kleiser schrieb am Sonntag, 16. Februar 2014 20:28:00 Uhr:

    Bei unseren Kundenprojekte kommt es häufiger vor, dass nach Jahren wieder ein Kunde eine Änderung an seiner Software haben will. Oder dass ein anderer Kunde ganz etwas ähnliches haben will. Wenn man da Unit-Tests schon für den alten Code hat, versteht man oft schneller die Anforderungen von damals als mit einer schlechten Doku.
    Unit-Tests helfen auch neuen Entwicklern im Projekt rein zu kommen.
    Man sollte es natürlich vernünftig und nicht dogmatisch betrieben.
    Ich kenne z.B: eine Firma die sehr auf Test-Abdeckung geachtet hatte, das aber heute etwas pragmatischer handhabt.


« Zurück  |  Weiter »