Как мне наследовать абстрактные модульные тесты в Ruby? - PullRequest
2 голосов
/ 02 апреля 2010

У меня есть два модульных теста, в которых должно быть много общих тестов с немного отличающимися методами настройки. Если я напишу что-то вроде

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

тогда Concrete1, похоже, не наследует тесты от Abstract. Или, по крайней мере, я не могу заставить их бежать в затмении. Если я выберу «Run all TestCases» для файла, который содержит Concrete1, тогда Abstract будет запущен, хотя я этого не хочу. Если я укажу Concrete1, то он вообще не запускает никаких тестов! Если я указываю test_1 в Concrete1, он жалуется, что не может его найти («uncaught throw: invalid_test (ArgumentError)»).

Я новичок в Ruby. Что мне здесь не хватает?

Ответы [ 2 ]

7 голосов
/ 02 апреля 2010

Проблема в том, что, насколько я могу судить, Test::Unit отслеживает, какие классы наследуются от Test::Unit::TestCase, и в результате только будет запускать тесты из классов, напрямую наследуется от него.

Способ обойти это - создать модуль с нужными вам тестами, а затем включить этот модуль в классы, производные от 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

Выход:

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
1 голос
/ 22 октября 2014

Проблема в том, что Test::Unit::TestCase явно не запускает тесты, определенные в суперклассах по умолчанию. В частности, обратите внимание, что TestSuiteCreator не запускает тесты, если Test::Unit::TestCase#valid? не возвращает 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

А что определяет, допустим ли тест? Тестовый пример действителен по умолчанию, если класс this явно определил этот метод или если метод был определен в 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

Таким образом, если вы создаете подкласс другого класса юнит-тестов и хотите запустить юнит-тесты суперкласса, вы можете:

  • Переопределите эти методы тестирования в своем подклассе, и пусть они вызовут метод тестирования вашего суперкласса
  • Переместите все ваши методы в модуль (как объяснено в другом ответе в этой теме)
  • Переопределите метод valid? в вашем подклассе, чтобы вернуть true:

def valid? return true end

...