Указатель NULL
- это тот, который указывает на никуда. Когда вы разыскиваете указатель p
, вы говорите «дайте мне данные в месте, хранящемся в« p ». Когда p
является нулевым указателем, местоположение, хранящееся в p
, является nowhere
, вы говорите «Дайте мне данные в месте« нигде ». Очевидно, он не может этого сделать, поэтому он выбрасывает NULL pointer exception
.
В общем, это потому, что что-то не было правильно инициализировано.
Это - то, как это, как предполагается, работает!
Каждый тест должен быть полностью изолирован от остальных, таким образом, 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)
Тест:: Единица документация документация дает хорошее объяснение о том, как работают комплекты.
Я столкнулся с этой точной проблемой и создал подкласс 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
, не будет назван, потому что он предполагает, что все методы тестирования будут выполнены в конечном счете.
Используйте TestSuite, как @romulo-a-ceccon описано для специальной подготовки к каждому набору тестов.
Однако я думаю, что нужно упомянуть здесь, что Модульные тесты являются ment для выполнения в общей изоляции. Таким образом поток выполнения является тестовым разрушением установки, которое должно гарантировать, что каждый тестовый прогон, без помех чем-либо другие тесты, сделал.
Я создал смешивание под названием 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
Сноски:
Как упомянуто в книге Хэла Фултона "Рубиновый путь". Он переопределяет метод 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
Что ж, я проделал в основном то же самое, очень уродливым и ужасным способом, но это было быстрее. :) Однажды я понял, что тесты запускаются в алфавитном порядке:
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
Неприятно, но работает :)