В Тесте Ruby:: Единица:: TestCase, как я переопределяю инициализировать метод?

Указатель NULL - это тот, который указывает на никуда. Когда вы разыскиваете указатель p, вы говорите «дайте мне данные в месте, хранящемся в« p ». Когда p является нулевым указателем, местоположение, хранящееся в p, является nowhere, вы говорите «Дайте мне данные в месте« нигде ». Очевидно, он не может этого сделать, поэтому он выбрасывает NULL pointer exception.

В общем, это потому, что что-то не было правильно инициализировано.

26
задан Andrew Grimm 11 October 2011 в 22:36
поделиться

6 ответов

Это - то, как это, как предполагается, работает!

Каждый тест должен быть полностью изолирован от остальных, таким образом, setup и tear_down методы выполняются однажды для каждого тестового сценария. Существуют случаи, однако, когда Вы могли бы хотеть больше управления потоком выполнения. Тогда можно сгруппировать тестовые сценарии в комплекты .

В случае, который Вы могли записать чему-то как следующее:

require 'test/unit'
require 'test/unit/ui/console/testrunner'

class TestDecorator < Test::Unit::TestSuite

  def initialize(test_case_class)
    super
    self << test_case_class.suite
  end

  def run(result, &progress_block)
    setup_suite
    begin
      super(result, &progress_block)      
    ensure
      tear_down_suite
    end
  end

end

class MyTestCase < Test::Unit::TestCase

  def test_1
    puts "test_1"
    assert_equal(1, 1)
  end

  def test_2
    puts "test_2"
    assert_equal(2, 2)
  end

end

class MySuite < TestDecorator

  def setup_suite
    puts "setup_suite"
  end

  def tear_down_suite
    puts "tear_down_suite"
  end

end

Test::Unit::UI::Console::TestRunner.run(MySuite.new(MyTestCase))

Эти TestDecorator определяет специальный комплект, который обеспечивает setup и tear_down метод, которые работают только однажды и за выполнением набора тестовых сценариев, которые это содержит.

недостаток этого состоит в том, что необходимо сказать Тест:: Единица , как запустить тесты в единице. В конечном счете Ваша единица содержит много тестовых сценариев, и Вам нужен декоратор для только одного из них, Вам будет нужно что-то вроде этого:

require 'test/unit'
require 'test/unit/ui/console/testrunner'

class TestDecorator < Test::Unit::TestSuite

  def initialize(test_case_class)
    super
    self << test_case_class.suite
  end

  def run(result, &progress_block)
    setup_suite
    begin
      super(result, &progress_block)      
    ensure
      tear_down_suite
    end
  end

end

class MyTestCase < Test::Unit::TestCase

  def test_1
    puts "test_1"
    assert_equal(1, 1)
  end

  def test_2
    puts "test_2"
    assert_equal(2, 2)
  end

end

class MySuite < TestDecorator

  def setup_suite
    puts "setup_suite"
  end

  def tear_down_suite
    puts "tear_down_suite"
  end

end

class AnotherTestCase < Test::Unit::TestCase

  def test_a
    puts "test_a"
    assert_equal("a", "a")
  end

end

class Tests

  def self.suite
    suite = Test::Unit::TestSuite.new
    suite << MySuite.new(MyTestCase)
    suite << AnotherTestCase.suite
    suite
  end

end

Test::Unit::UI::Console::TestRunner.run(Tests.suite)

Тест:: Единица документация документация дает хорошее объяснение о том, как работают комплекты.

10
ответ дан Rômulo Ceccon 28 November 2019 в 07:02
поделиться

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

Вот то, что я придумал. Это обеспечивает свой собственный setup и teardown методы, которые считают количество методов в классе, которые начинаются с 'теста'. На первом вызове к setup это звонит global_setup, и на последней возможности к teardown это звонит global_teardown

class ImprovedUnitTestCase < Test::Unit::TestCase
  cattr_accessor :expected_test_count

  def self.global_setup; end
  def self.global_teardown; end    

  def teardown
    if((self.class.expected_test_count-=1) == 0)
      self.class.global_teardown
    end
  end
  def setup
    cls = self.class

    if(not cls.expected_test_count)
      cls.expected_test_count = (cls.instance_methods.reject{|method| method[0..3] != 'test'}).length
      cls.global_setup
    end
  end
end

, Создают Ваши тестовые сценарии как это:

class TestSomething < ImprovedUnitTestCase
  def self.global_setup
    puts 'global_setup is only run once at the beginning'
  end

  def self.global_teardown
    puts 'global_teardown is only run once at the end'
  end

  def test_1 
  end

  def test_2
  end
end

отказ в этом - то, что Вы не можете обеспечить свое собственное самое дерзкое setup и teardown методы, если Вы не используете setup :method_name метод класса (только доступный в направляющих 2. X?) и если у Вас есть набор тестов или что-то, что только выполняет один из методов тестирования, тогда эти global_teardown, не будет назван, потому что он предполагает, что все методы тестирования будут выполнены в конечном счете.

1
ответ дан Daniel Beardsley 28 November 2019 в 07:02
поделиться

Используйте TestSuite, как @romulo-a-ceccon описано для специальной подготовки к каждому набору тестов.

Однако я думаю, что нужно упомянуть здесь, что Модульные тесты являются ment для выполнения в общей изоляции. Таким образом поток выполнения является тестовым разрушением установки, которое должно гарантировать, что каждый тестовый прогон, без помех чем-либо другие тесты, сделал.

0
ответ дан Honza 28 November 2019 в 07:02
поделиться

Я создал смешивание под названием SetupOnce. Вот пример использования его.

require 'test/unit'
require 'setuponce'


class MyTest < Test::Unit::TestCase
  include SetupOnce

  def self.setup_once
    puts "doing one-time setup"
  end

  def self.teardown_once
    puts "doing one-time teardown"
  end

end

И вот фактический код; заметьте, что это требует другого модуля, доступного от первой ссылки в сносках.

require 'mixin_class_methods' # see footnote 1

module SetupOnce
  mixin_class_methods

  define_class_methods do
    def setup_once; end

    def teardown_once; end

    def suite
      mySuite = super

      def mySuite.run(*args)
        @name.to_class.setup_once
        super(*args)
        @name.to_class.teardown_once
      end

      return mySuite
    end
  end
end

# See footnote 2
class String
  def to_class
    split('::').inject(Kernel) {
      |scope, const_name|
      scope.const_get(const_name)
    }
  end
end

Сноски:

  1. http://redcorundum.blogspot.com/2006/06/mixing-in-class-methods.html

  2. http://infovore.org/archives/2006/08/02/getting-a-class-object-in-ruby-from-a-string-containing-that-classes-name/

0
ответ дан 28 November 2019 в 07:02
поделиться

Как упомянуто в книге Хэла Фултона "Рубиновый путь". Он переопределяет метод self.suite класса Test :: Unit, который позволяет запускать тесты в классе как набор.

def self.suite
    mysuite = super
    def mysuite.run(*args)
      MyTest.startup()
      super
      MyTest.shutdown()
    end
    mysuite
end

Вот пример:

class MyTest < Test::Unit::TestCase
    class << self
        def startup
            puts 'runs only once at start'
        end
        def shutdown
            puts 'runs only once at end'
        end
        def suite
            mysuite = super
            def mysuite.run(*args)
              MyTest.startup()
              super
              MyTest.shutdown()
            end
            mysuite
        end
    end

    def setup
        puts 'runs before each test'
    end
    def teardown
        puts 'runs after each test'
    end 
    def test_stuff
        assert(true)
    end
end
26
ответ дан 28 November 2019 в 07:02
поделиться

Что ж, я проделал в основном то же самое, очень уродливым и ужасным способом, но это было быстрее. :) Однажды я понял, что тесты запускаются в алфавитном порядке:

class MyTests < Test::Unit::TestCase
def test_AASetup # I have a few tests that start with "A", but I doubt any will start with "Aardvark" or "Aargh!"
    #Run setup code
end

def MoreTests
end

def test_ZTeardown
    #Run teardown code
end

Неприятно, но работает :)

2
ответ дан 28 November 2019 в 07:02
поделиться
Другие вопросы по тегам:

Похожие вопросы: