用Ruby';s Test::Unit::TestCase,如何重写initialize方法?
我正在与Test::Unit进行斗争。当我想到单元测试时,我想到的是每个文件一个简单的测试。但在Ruby的框架中,我必须写:用Ruby';s Test::Unit::TestCase,如何重写initialize方法?,ruby,unit-testing,testunit,Ruby,Unit Testing,Testunit,我正在与Test::Unit进行斗争。当我想到单元测试时,我想到的是每个文件一个简单的测试。但在Ruby的框架中,我必须写: class MyTest < Test::Unit::TestCase def setup end def test_1 end def test_1 end end classmytest
class MyTest < Test::Unit::TestCase
def setup
end
def test_1
end
def test_1
end
end
classmytest
但是每次调用test_*方法都会运行setup和teardown。这正是我不想要的。相反,我想要一个对整个类只运行一次的setup方法。但是如果不破坏TestCase的initialize,我似乎无法编写自己的initialize()
可能吗?还是我把这件事搞得太复杂了?应该是这样的 每个测试都应该与其他测试完全隔离,因此对每个测试用例执行一次
设置
和拆卸
方法。但是,在某些情况下,您可能需要对执行流进行更多控制。然后您可以将测试用例分组到套件中
在您的情况下,您可以编写如下内容:
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))
文档很好地解释了套件是如何工作的。我遇到了这个问题,并创建了
Test::Unit::TestCase
的子类,以实现您所描述的功能
这是我想到的。它提供了自己的setup
和teardown
方法,用于计算类中以“test”开头的方法的数量。第一次调用setup
时,它调用global\u setup
,最后一次调用teardown
时,它调用global\u 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 ImprovedUnitTestCase
像这样创建您的测试用例:
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)
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
class TestSomething
这其中的错误在于,除非您使用
setup:method\u name
class方法(仅在Rails 2.X中可用),并且如果您有一个测试套件或仅运行其中一个测试方法的东西,否则您无法提供自己的每测试setup
和teardown
方法,然后,将不会调用全局\u teardown
,因为它假定所有的测试方法最终都将运行。使用TestSuite作为@romulo-a-ceccon,用于每个测试套件的特殊准备
然而,我认为这里应该提到,单元测试是完全隔离运行的。因此,执行流是setup-test-teardown,它应该保证每个测试的运行不会受到其他测试所做的任何事情的干扰。我创建了一个名为SetupOnce的mixin。下面是一个使用它的示例
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
脚注:
正如哈尔·富尔顿在《红宝石之路》一书中提到的那样。 他重写了Test::Unit的self.suite方法,该方法允许类中的测试用例作为套件运行
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
classmytest 班级嗯,我基本上以同样的方式完成了一个非常丑陋和可怕的任务,但速度更快。:)一旦我意识到测试是按字母顺序运行的:
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
classmytests
它虽然不漂亮,但很有效:)为了解决这个问题,我使用了setup结构,只使用了一种测试方法。这个testmethod正在调用所有其他测试
比如说
class TC_001 << Test::Unit::TestCase
def setup
# do stuff once
end
def testSuite
falseArguments()
arguments()
end
def falseArguments
# do stuff
end
def arguments
# do stuff
end
end
@orion edwards的上述RSpec答案的TC_001级+1。我本来会对他的答案发表评论的,但我还没有足够的声誉来评论他的答案
我经常使用test/unit和RSpec,我不得不说。。。每个人发布的代码都缺少(:all)
之前的一个非常重要的特性:@instance variable support
在RSpec中,您可以执行以下操作:
描述“无论做什么”
以前:所有人都这样做
@foo='foo'
结束
#这会过去的
这是“第一个”做的
断言_等于'foo',@foo
@foo=‘不同’
断言_等于‘不同’,@foo
结束
#即使之前的测试更改了
#@foo的值。这是因为RSpec存储
#由before(:all)和copies创建的所有实例变量
#在每次测试运行之前,将它们放入测试的范围。
这是“第二次”做什么
断言_等于'foo',@foo
@foo=‘不同’
断言_等于‘不同’,@foo
结束
结束
#startup
和#shutdown
的实现首先关注于确保这些方法对于整个TestCase
类只调用一次,但是这些方法中使用的任何实例变量都将丢失
RSpec在自己的对象实例中运行(:all)之前的,并且在运行每个测试之前复制所有局部变量
要访问在全局启动过程中创建的任何变量,您需要:
- 复制由
#startup
创建的所有实例变量,就像RSpec一样
- 将
#startup
中的变量定义到一个可以从测试方法访问的范围内,例如@@class_variables
或创建提供对的访问的类级attr_访问器
class TC_001 << Test::Unit::TestCase
def setup
# do stuff once
end
def testSuite
falseArguments()
arguments()
end
def falseArguments
# do stuff
end
def arguments
# do stuff
end
end
Test::Unit.at_start do
# initialization stuff here
end
class MyTest < Test::Unit::TestCase
@@cmptr = nil
def setup
if @@cmptr.nil?
@@cmptr = 0
puts "runs at first test only"
@@var_shared_between_fcs = "value"
end
puts 'runs before each test'
end
def test_stuff
assert(true)
end
end