2010-04-01 5 views
2

J'ai deux tests unitaires qui devraient partager beaucoup de tests communs avec des méthodes d'installation légèrement différentes. Si j'écris quelque chose commeComment hériter des tests unitaires abstraits dans Ruby?

class Abstract < Test::Unit::TestCase 
    def setup 
    @field = create 
    end 

    def test_1 
    ... 
    end 
end 

class Concrete1 < Abstract 
    def create 
    SomeClass1.new 
    end 
end 

class Concrete2 < Abstract 
    def create 
    SomeClass2.new 
    end 
end 

alors Concrete1 ne semble pas hériter des tests de Abstract. Ou au moins je ne peux pas les faire fonctionner en éclipse. Si je choisis "Run all TestCases" pour le fichier contenant Concrete1 alors Abstract est exécuté même si je ne le veux pas. Si je spécifie Concrete1 alors il ne fait aucun test du tout! Si je spécifie test_1 dans Concrete1 alors il se plaint qu'il ne peut pas le trouver ("uncaught throw: invalid_test (ArgumentError)").

Je suis nouveau à Ruby. Qu'est-ce que j'oublie ici?

Répondre

7

Le problème est que, pour autant que je peux dire, Test::Unit garde la trace des classes héritent de Test::Unit::TestCase, et par conséquent, la volonté seulement exécuter des tests de classes qui directement en héritent.

La façon de contourner ce problème est de créer un module avec les tests que vous voulez, puis inclure ce module dans les classes qui dérivent de Test::Unit::TestCase.

require 'test/unit' 

module TestsToInclude 
    def test_name 
    assert(self.class.name.start_with?("Concrete")) 
    end 
end 

class Concrete1 < Test::Unit::TestCase 
    include TestsToInclude 

    def test_something_bad 
    assert(false) 
    end 
end 

class Concrete2 < Test::Unit::TestCase 
    include TestsToInclude 

    def test_something_good 
    assert(true) 
    end 
end 

Sortie:

 
Loaded suite a 
Started 
.F.. 
Finished in 0.027873 seconds. 

    1) Failure: 
test_something_bad(Concrete1) [a.rb:13]: 
<false> is not true. 

4 tests, 4 assertions, 1 failures, 0 errors 

shell returned 1 
+0

Merci - ce qui est excellent! Il m'a aidé à résoudre enfin http://stackoverflow.com/questions/8888614/how-to-write-and-inherit-from-an-abstract-subclass-of-actioncontrollertestcasecase qui est défini dans le contexte de Rails fonctionnel (contrôleur) tests, et en tant que tel a un petit défi supplémentaire. –

+0

Une autre observation: malheureusement, il semble que cette approche ne permet pas de passer outre les tests inclus. Je suppose qu'un autre hack sera nécessaire pour contourner cela :-( –

+0

Cela n'a pas de sens pour moi Si les classes de test doivent directement hériter de Test :: Unit :: TestCase, comment ActiveSupport :: TestCase et ActionController :: TestCase pas causer le problème décrit dans cette question?Ne sont-ils pas tous deux des niveaux d'héritage supplémentaires entre Test :: Unit :: TestCase et les classes concrètes? –

1

Le problème est que Test::Unit::TestCase ne explicitement pas exécuté des tests définis dans superclasse par défaut. En particulier, notez que TestSuiteCreator ne court pas de tests à moins Test::Unit::TestCase#valid? renvoie true (https://github.com/test-unit/test-unit/blob/2.5.5/lib/test/unit/testsuitecreator.rb#L40):

def append_test(suite, test_name) 
    test = @test_case.new(test_name) 
    yield(test) if block_given? 
    suite << test if test.valid? 
end 

Et ce qui détermine si un test est valide? Un cas de test est valide par défaut si la cette classe définit explicitement cette méthode, ou si la méthode a été définie dans un Module (https://github.com/test-unit/test-unit/blob/2.5.5/lib/test/unit/testcase.rb#L405-L418):

def valid? # :nodoc: 
    return false unless respond_to?(@method_name) 
    test_method = method(@method_name) 
    if @internal_data.have_test_data? 
    return false unless test_method.arity == 1 
    else 
    return false unless test_method.arity <= 0 
    end 
    owner = Util::MethodOwnerFinder.find(self, @method_name) 
    if owner.class != Module and self.class != owner 
    return false 
    end 
    true 
end 

Donc, fondamentalement, si vous sous-classe une autre classe de test unitaire, et que vous voulez pour exécuter les tests unitaires de superclasse, vous pouvez:

  • Redéfinir les méthodes d'essai dans votre sous-classe et de les appeler la méthode de test de votre superclasse
  • Déplacer toutes vos méthodes à un module (comme expliqué dans l'autre réponse ce fil)
  • Redéfinir la méthode valid? dans votre sous-classe pour retourner vrai:

def valid? return true end