3 Stimmen

Wie lässt sich effizienter Code durch Unit-Tests erstellen?

Ich nehme an einem TDD Coding Dojo teil, wo wir versuchen, reines TDD an einfachen Problemen zu üben. Dabei ist mir aufgefallen, dass der Code, der aus den Unit-Tests hervorgeht, nicht der effizienteste ist. Das ist in den meisten Fällen in Ordnung, aber was ist, wenn der Codeverbrauch so wächst, dass die Effizienz zu einem Problem wird?

Ich liebe die Art und Weise der Code entsteht aus Unit-Tests, aber ist es möglich, die Effizienz Eigenschaft entstehen durch weitere Tests?

Hier ist ein triviales Beispiel in Ruby: Primfaktorzerlegung. Ich habe einen reinen TDD-Ansatz verfolgt, bei dem die Tests einen nach dem anderen bestanden haben und meinen ursprünglichen Akzeptanztest (unten kommentiert) validiert haben. Welche weiteren Schritte könnte ich unternehmen, wenn ich einen der Tests generische Algorithmen zur Primfaktorzerlegung auftauchen? Um den Problembereich einzugrenzen, sagen wir, ich möchte eine quadratisches Sieb Umsetzung ... In diesem konkreten Fall kenne ich den "optimalen Algorithmus", aber in den meisten Fällen wird der Kunde einfach eine Anforderung hinzufügen, dass die Funktion in einer bestimmten Umgebung in weniger als "x" Zeit läuft.

require 'shoulda'
require 'lib/prime'

class MathTest < Test::Unit::TestCase
  context "The math module" do
    should "have a method to get primes" do 
      assert Math.respond_to? 'primes'
    end
  end
  context "The primes method of Math" do
    should "return [] for 0" do
      assert_equal [], Math.primes(0)
    end
    should "return [1] for 1 " do
      assert_equal [1], Math.primes(1)
    end
    should "return [1,2] for 2" do 
      assert_equal [1,2], Math.primes(2)
    end
    should "return [1,3] for 3" do 
      assert_equal [1,3], Math.primes(3)
    end
    should "return [1,2] for 4" do 
      assert_equal [1,2,2], Math.primes(4)
    end 
    should "return [1,5] for 5" do 
      assert_equal [1,5], Math.primes(5)
    end   
    should "return [1,2,3] for 6" do 
      assert_equal [1,2,3], Math.primes(6)
    end       
    should "return [1,3] for 9" do 
      assert_equal [1,3,3], Math.primes(9)
    end        
    should "return [1,2,5] for 10" do 
      assert_equal [1,2,5], Math.primes(10)
    end                  
  end
#  context "Functionnal Acceptance test 1" do
#    context "the prime factors of 14101980 are 1,2,2,3,5,61,3853"do      
#      should "return  [1,2,3,5,61,3853] for ${14101980*14101980}" do
#        assert_equal [1,2,2,3,5,61,3853], Math.primes(14101980*14101980)
#      end
#    end
#  end
end

und der naive Algorithmus, den ich mit diesem Ansatz entwickelt habe

module Math
  def self.primes(n)
    if n==0
      return []
    else
      primes=[1]  
      for i in 2..n do
        if n%i==0          
          while(n%i==0)
            primes<<i
            n=n/i
          end
        end
      end      
      primes
    end
  end
end

bearbeiten 1 Nach den ersten Antworten zu urteilen, habe ich mich in meiner ursprünglichen Beschreibung wohl nicht klar ausgedrückt: Der Leistungstest ist nicht ein Standardbestandteil meines Einheitstests, es ist ein neue Abnahmeprüfung geschrieben als Antwort auf eine spezielle Anforderung vom Kunden.

bearbeiten 2 Ich weiß, wie man die Ausführungszeit testet, aber es scheint, dass der Übergang vom trivialen Algorithmus zum optimierten Algorithmus ein großer Schritt ist. auftauchen Oder anders ausgedrückt: Wie zerlegt man die Migration vom trivialen Code zum optimalen Code? Einige erwähnten, dass es sich um einen problemspezifischen Ansatz handelt: Ich habe ein Beispielproblem angegeben, bei dem ich nicht weiß, wie es weitergehen soll.

0voto

stonemetal Punkte 6058

Führen Sie einen Effizienztest durch. Sie geben die Anforderung vor: "Die Funktion muss in einer gegebenen Umgebung in weniger als "x" Zeit laufen." Schreiben Sie einen Test, der die Ausführungszeit prüft. Wenn Sie den Test bestehen, besteht keine Notwendigkeit für weitere Codeverbesserungen, wenn er fehlschlägt, machen Sie ihn schneller, entweder durch Profiling und Mikro-Optionen oder algorithmische Verbesserungen.

Ich muss BlueRaja zustimmen, dass Leistungstests kein Standardbestandteil Ihrer Unit-Tests sein sollten, aber wenn der Schwerpunkt auf Leistung liegt, kann es helfen, sie auf dem Tisch zu halten.

0voto

ndp Punkte 20756

Ich dachte zunächst, dies würde nicht funktionieren und Sie brauchten einen größeren Sprung als TDD leisten könnte, und ich wollte sagen, zumindest Ihre Tests werden Ihnen helfen, wie Sie Ihren Code neu zu schreiben.

Aber Sie sollten es versuchen und uns Bescheid geben. Obwohl ich es nicht getan habe, denke ich, dass Ihr nächster Test sollte sein einen Leistungstest. Dies ist derzeit eindeutig eine Anforderung, warum also nicht in diesem Sinne weitermachen?

Ich denke, Sie können ein Programm schreiben, das auf jeder Plattform zuverlässig läuft, wenn Sie eine Basislinie festlegen. Sie werden eine gewisse Infrastruktur benötigen, um Ihnen zu helfen, aber es kann so aussehen:

TEST: should be faster than O(n^2)
setup: baseline_time_for_10 = time_of( f(10) )
100: assert time_of(f(100)) < baseline_time_for_10 ^ 2    
etc.

Ich wollte das schon immer tun, hatte aber noch nicht die richtige Gelegenheit für ein Projekt. Lassen Sie uns wissen, wie es läuft.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X