Frage

Ich stelle eine Präsentation über die Vorteile von Unit-Tests zusammen und ich würde ein einfaches Beispiel der unbeabsichtigten Folgen wie: Ändern von Code in einer Klasse, die Pausen-Funktionalität in einer anderen Klasse.

vorschlagen kann jemand eine einfache, leicht, ein Beispiel dafür zu erklären?

Mein Plan ist, zu schreiben Unit-Tests um diese Funktionalität zu zeigen, dass wir wissen, dass wir etwas brach, indem sie unverzüglich den Test ausgeführt wird.

War es hilfreich?

Lösung

Eine etwas einfachere und damit vielleicht klarer, Beispiel:

public string GetServerAddress()
{
    return "172.0.0.1";
}

public void DoSomethingWithServer()
{
    Console.WriteLine("Server address is: " +  GetServerAddress());
}

Wenn GetServerAddress ist Änderungen ein Array zurück:

public string[] GetServerAddress()
{
    return new string[] { "127.0.0.1", "localhost" };
}

Die Ausgabe von DoSomethingWithServer wird etwas anders sein, aber es wird alles noch kompilieren, für einen noch subtilere Fehler zu machen.

Die erste (nicht-Array) Version druckt Server address is: 127.0.0.1 und die zweite wird Server address is: System.String[] drucken, das etwas ist, ich habe auch in der Produktion Code gesehen. Unnötig zu sagen, es ist nicht mehr da!

Andere Tipps

Hier ist ein Beispiel:

class DataProvider {
    public static IEnumerable<Something> GetData() {
        return new Something[] { ... };
    }
}

class Consumer {
    void DoSomething() {
        Something[] data = (Something[])DataProvider.GetData();
    }
}

Ändern GetData() eine List<Something> zurückzukehren, und Consumer brechen.

Dies könnte ein wenig gekünstelt gesehen, aber ich habe in echtem Code ähnliche Probleme gesehen.

Sagen haben Sie eine Methode, die funktioniert:

abstract class ProviderBase<T>
{
  public IEnumerable<T> Results
  {
    get
    {
      List<T> list = new List<T>();
      using(IDataReader rdr = GetReader())
        while(rdr.Read())
          list.Add(Build(rdr));
      return list;
    }
  }
  protected abstract IDataReader GetReader();
  protected T Build(IDataReader rdr);
}

Mit verschiedenen Implementierungen verwendet werden. Einer von ihnen wird eingesetzt in:

public bool CheckNames(NameProvider source)
{
  IEnumerable<string> names = source.Results;
  switch(names.Count())
  {
      case 0:
        return true;//obviously none invalid.
      case 1:
        //having one name to check is a common case and for some reason
        //allows us some optimal approach compared to checking many.
        return FastCheck(names.Single());
      default:
        return NormalCheck(names)
  }
}

Nun, nichts davon ist besonders seltsam. Wir sind nicht eine bestimmte implementaiton von IEnumerable annimmt. Tatsächlich wird dies für Arrays arbeiten und sehr viele häufig verwendete Sammlungen (kann nicht von einem in System.Collections.Generic, die nicht die Spitze von meinem Kopf passen off der Fall ist). Wir haben nur die normalen Methoden, und die normalen Erweiterungsmethoden verwendet. Es ist nicht einmal ungewöhnlich, dass eine optimierte Fall für Einzelposten Sammlungen zu haben. Wir könnten die Liste zum Beispiel Änderung ein Array sein, oder vielleicht ein HashSet (automatisch Duplikate entfernen) oder eine LinkedList oder ein paar andere Dinge, und es wird weiter arbeiten.

Noch während wir uns nicht auf eine bestimmte Implementierung abhängig sind, sind wir abhängig von einem bestimmten Merkmal, insbesondere des Seins wiederwickelbarer (Count() entweder ICollection.Count oder sonst enumerate durch die zählbare nennen, nach dem der Name Prüfung stattfinden wird.

Someone obwohl sieht Ergebnisse Eigenschaft und denkt „hmm, das ist ein bisschen verschwenderisch“. Sie ersetzen es mit:

public IEnumerable<T> Results
{
  get
  {
    using(IDataReader rdr = GetReader())
      while(rdr.Read())
        yield return Build(rdr);
  }
}

Dies wieder vollkommen in Ordnung, und wird in der Tat zu einer erheblichen Leistungssteigerung in vielen Fällen führen. Wenn CheckNames nicht durch die Codierer in Frage in den unmittelbaren „Tests“ getan getroffen wird (vielleicht ist es nicht in einer Menge von Codepfaden treffen), dann der Tatsache, dass CheckNames Fehler (und möglicherweise ein falsches Ergebnis in dem Fall zurückkehren von mehr als 1 Namen, der noch schlimmer sein kann, wenn es ein Sicherheitsrisiko öffnet).

Jeder Unit-Test, dass Zugriffe auf CheckNames mit den mehr als Null Ergebnissen geht, obwohl es zu fangen.


übrigens eine vergleichbare (wenn mehr kompliziert) Änderung ist ein Grund für eine Rückwärtskompatibilität Feature in Npgsql. Nicht ganz so einfach wie nur ein List.Add () mit einer Rück Ausbeute zu ersetzen, sondern eine Veränderung die Art und Weise ExecuteReader gearbeitet hat einen vergleichbaren Wechsel von O (n) bis O (1), um das erste Ergebnis zu erhalten. Doch bevor dann NpgsqlConnection erlaubt den Benutzern einen anderen Leser von einer Verbindung zu erhalten, während der erste noch offen war, und nachdem es nicht. Die Dokumentation für IDbConnection sagt, Sie sollten dies nicht tun, aber das bedeutet nicht dort kein fließendes Code war das tat. Zum Glück ein solches Stück Code des Laufens war ein NUnit-Test und eine Rückwärtskompatibilität Funktion hinzugefügt solchen Code zu ermöglichen Funktion mit nur einer Änderung der Konfiguration fortzufahren.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top