test4requirements 0.1.0.alpha.2
Sign up to get free protection for your applications and to get access to all the features.
- data/examples/example_test4requirements.rb +62 -0
- data/examples/example_test4requirements_shoulda.rb +60 -0
- data/lib/test4requirements.rb +98 -0
- data/lib/test4requirements/requirement.rb +132 -0
- data/lib/test4requirements/requirementlist.rb +163 -0
- data/lib/test4requirements/shoulda.rb +77 -0
- data/lib/test4requirements/testcase.rb +59 -0
- data/unittest/check_compatibility.rb +65 -0
- data/unittest/unittest_requirement.rb +82 -0
- data/unittest/unittest_requirementlist.rb +84 -0
- data/unittest/unittest_requirementlist_overview.rb +199 -0
- data/unittest/unittest_shoulda.rb +123 -0
- data/unittest/unittest_testcase.rb +44 -0
- metadata +133 -0
@@ -0,0 +1,62 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
Some examples, how you can use test4requirements in your test.
|
3
|
+
|
4
|
+
Expectes Testresults:
|
5
|
+
req1 successfull
|
6
|
+
req2 tested, but without success
|
7
|
+
req3 no test
|
8
|
+
req4 successfull
|
9
|
+
=end
|
10
|
+
gem 'test-unit'#, '= 2.1.1'
|
11
|
+
|
12
|
+
$:.unshift('../lib')
|
13
|
+
require 'test4requirements.rb'
|
14
|
+
|
15
|
+
$req = Test4requirements::RequirementList.new(:req1,:req2,:req3, :req4)
|
16
|
+
#~ $req.log.outputters << Log4r::StdoutOutputter.new('stdout')
|
17
|
+
#Define a user defined action after test execution.
|
18
|
+
$req.do_after_tests{|reqlist|
|
19
|
+
puts reqlist.overview(nil)
|
20
|
+
}
|
21
|
+
|
22
|
+
=begin rdoc
|
23
|
+
Example code
|
24
|
+
=end
|
25
|
+
module Examples
|
26
|
+
=begin rdoc
|
27
|
+
Test requirements 1 and 2.
|
28
|
+
=end
|
29
|
+
class Test_requirement_1_2 < Test::Unit::TestCase
|
30
|
+
#Following requirements exist, and must be tested sucessfull
|
31
|
+
requirements $req
|
32
|
+
|
33
|
+
#Test requirement 1.
|
34
|
+
def test_1()
|
35
|
+
assign_requirement(:req1) #this test is testing requirement 1
|
36
|
+
assert_equal(2,1+1)
|
37
|
+
end
|
38
|
+
#Test requirement 2.
|
39
|
+
def test_2()
|
40
|
+
assign_requirement(:req2)
|
41
|
+
assert_equal(3,1+1)
|
42
|
+
end
|
43
|
+
#Test requirement 3, but without assignment to a requirement
|
44
|
+
def test_3()
|
45
|
+
#no assignment to requirement 3
|
46
|
+
pend 'pend'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
=begin rdoc
|
51
|
+
Test requirement 4.
|
52
|
+
=end
|
53
|
+
class Test_requirement_4 < Test::Unit::TestCase
|
54
|
+
#Following requirements exist, and must be tested sucessfull
|
55
|
+
requirements $req
|
56
|
+
#Test requirement 4.
|
57
|
+
def test_4()
|
58
|
+
assign_requirement(:req4) #this test is testing requirement 4
|
59
|
+
assert_equal(2,1+1)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end #module Examples
|
@@ -0,0 +1,60 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
Example code
|
3
|
+
=end
|
4
|
+
|
5
|
+
$:.unshift('../lib')
|
6
|
+
gem 'test-unit'#, '= 2.1.1'
|
7
|
+
|
8
|
+
require 'test4requirements'
|
9
|
+
require 'test4requirements/shoulda'
|
10
|
+
#~ require 'shoulda'
|
11
|
+
|
12
|
+
$req = Test4requirements::RequirementList.new(:req1,:req2,:req3, :req4, :req5)
|
13
|
+
#~ $req.log.outputters << Log4r::StdoutOutputter.new('stdout')
|
14
|
+
|
15
|
+
=begin rdoc
|
16
|
+
Example code
|
17
|
+
=end
|
18
|
+
module Examples
|
19
|
+
=begin rdoc
|
20
|
+
Some examples, how you can use test4requirements with shoulda.
|
21
|
+
|
22
|
+
Expected result:
|
23
|
+
|
24
|
+
Requirement req1 was successfull tested (fullfill request 1 (req. of customer X/Examples::Test_with_shoulda))
|
25
|
+
Requirement req2 was unsuccessfull tested (fullfill request 2 (req. of customer X/Examples::Test_with_shoulda))
|
26
|
+
Requirement req3 was successfull tested (request_3 (Examples::_with_shoulda/Examples::Test_with_shoulda))
|
27
|
+
Requirement req4 was successfull tested (request_4 (Examples::_with_shoulda/Examples::Test_with_shoulda))
|
28
|
+
Requirement req5 was not tested
|
29
|
+
=end
|
30
|
+
class Test_with_shoulda < Test::Unit::TestCase
|
31
|
+
#Following requirements exist, and must be tested sucessfull
|
32
|
+
requirements $req
|
33
|
+
|
34
|
+
context 'req. of customer X' do
|
35
|
+
#Add requirement as parameter of should
|
36
|
+
should 'fullfill request 1', requirement: :req1 do
|
37
|
+
assert_equal(2,1+1)
|
38
|
+
end
|
39
|
+
#add requirement via requirement command
|
40
|
+
should 'fullfill request 2' do
|
41
|
+
assign_requirement(:req2) #this test is testing requirement 1
|
42
|
+
assert_equal(3,1+1)
|
43
|
+
end
|
44
|
+
end #context
|
45
|
+
|
46
|
+
#Examples outside a context
|
47
|
+
|
48
|
+
#Add requirement as parameter of should
|
49
|
+
should 'request_3', requirement: :req3 do
|
50
|
+
assert_equal(2,1+1)
|
51
|
+
end
|
52
|
+
|
53
|
+
#add requirement via requirement command
|
54
|
+
should 'request_4' do
|
55
|
+
assign_requirement(:req4) #this test is testing requirement 1
|
56
|
+
assert_equal(2,1+1)
|
57
|
+
end
|
58
|
+
|
59
|
+
end #MyTest_shoulda
|
60
|
+
end #module Examples
|
@@ -0,0 +1,98 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
This gem is based on a
|
3
|
+
{question at stackoverflow}[http://stackoverflow.com/questions/6958586/are-there-any-good-ruby-testing-traceability-solutions]:
|
4
|
+
|
5
|
+
:: Are there any gems that'll allow me to implement traceability from my tests back to designs/requirements?
|
6
|
+
|
7
|
+
:: i.e.: I want to tag my tests with the name of the requirements they test, and then generate reports of requirements that aren't tested or have failing tests, etc.
|
8
|
+
|
9
|
+
:title:test4requirements Unit-Testing with assignment to requirements
|
10
|
+
|
11
|
+
=Usage
|
12
|
+
require 'test4requirements.rb'
|
13
|
+
|
14
|
+
==Define RequirementList and Requirements
|
15
|
+
You must define your requirements and a RequirementList.
|
16
|
+
|
17
|
+
You can make it with simple keys and without description:
|
18
|
+
$req = Test4requirements::RequirementList.new(:req1,:req2,:req3, :req4)
|
19
|
+
|
20
|
+
You may set an outputter to a logger:
|
21
|
+
$req.log.outputters << Log4r::StdoutOutputter.new('stdout')
|
22
|
+
|
23
|
+
|
24
|
+
Or you can define an empty list and assign requirements
|
25
|
+
with description etc.
|
26
|
+
$req = Test4requirements::RequirementList.new()
|
27
|
+
$req << Test4requirements::Requirement.new(
|
28
|
+
description: 'What is required',
|
29
|
+
reference: 'Customer Requirement, Version 2011-08-02, page 12'
|
30
|
+
)
|
31
|
+
|
32
|
+
==Define your test cases
|
33
|
+
|
34
|
+
With Test::Unit::TestCase.requirments you can assign a RequirementList to your tests.
|
35
|
+
class Test_requirement < Test::Unit::TestCase
|
36
|
+
#Following requirements exist, and must be tested sucessfull
|
37
|
+
requirements $req
|
38
|
+
# ...
|
39
|
+
end
|
40
|
+
|
41
|
+
A RequirementList can be assigned to multiply TestCase.
|
42
|
+
|
43
|
+
==Assign tests to requirements
|
44
|
+
Tests can by assigned to a requirement with
|
45
|
+
Test::Unit::TestCase#assign_requirement.
|
46
|
+
|
47
|
+
#Test requirement 1.
|
48
|
+
def test_1()
|
49
|
+
assign_requirement(:req1) #this test is testing requirement 1
|
50
|
+
assert_equal(2,1+1)
|
51
|
+
end
|
52
|
+
|
53
|
+
==Get the result
|
54
|
+
|
55
|
+
Unit-Test are executed at_exit. Your results are not available before the tests are ready.
|
56
|
+
|
57
|
+
There are three ways to get information.
|
58
|
+
|
59
|
+
===Standard reporting
|
60
|
+
|
61
|
+
Like the Unit-Test results the requirement-result are written at the end
|
62
|
+
with Test4requirements::RequirementList#overview
|
63
|
+
|
64
|
+
You can influence the output with Test4requirements::RequirementList#report_type=
|
65
|
+
|
66
|
+
===Logger
|
67
|
+
There is a logger. You have access to teh logger with Test4requirements::RequirementList#log.
|
68
|
+
You can define outputters to the logger, you may log in a file to get more informations.
|
69
|
+
You can log to STDOUT, but the logging will be mixed with the test output.
|
70
|
+
|
71
|
+
===User defined actions
|
72
|
+
Each action must be done at the end of the script.
|
73
|
+
You may use Test4requirements::RequirementList#do_after_tests to define an
|
74
|
+
action at the end of the script.
|
75
|
+
|
76
|
+
=end
|
77
|
+
|
78
|
+
gem 'test-unit'
|
79
|
+
=begin
|
80
|
+
unittest 2.3.1:
|
81
|
+
Test::Unit::Assertions uses Encoding#ascii_compatible?
|
82
|
+
Is not defined in ruby 1.9.1 (at least my installation ;) )
|
83
|
+
=end
|
84
|
+
gem 'test-unit', '<= 2.1.1' if RUBY_VERSION == '1.9.1'
|
85
|
+
require 'test/unit'
|
86
|
+
|
87
|
+
module Test4requirements
|
88
|
+
VERSION = '0.1.0.alpha.2'
|
89
|
+
end
|
90
|
+
require 'log4r'
|
91
|
+
require_relative 'test4requirements/testcase'
|
92
|
+
require_relative 'test4requirements/requirement'
|
93
|
+
require_relative 'test4requirements/requirementlist'
|
94
|
+
|
95
|
+
#Not loaded by default.
|
96
|
+
#require_relative 'test4requirements/shoulda'
|
97
|
+
|
98
|
+
|
@@ -0,0 +1,132 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
Define a requirement.
|
3
|
+
|
4
|
+
=end
|
5
|
+
module Test4requirements
|
6
|
+
class Requirement
|
7
|
+
#Valid options for Requirement#new
|
8
|
+
OPTIONS = [
|
9
|
+
:description,
|
10
|
+
:reference,
|
11
|
+
]
|
12
|
+
=begin rdoc
|
13
|
+
Each requirement must include a key.
|
14
|
+
|
15
|
+
Optional values:
|
16
|
+
* description
|
17
|
+
* reference
|
18
|
+
=end
|
19
|
+
def initialize( key, options = {} )
|
20
|
+
@key = key
|
21
|
+
options.keys.each{|key|
|
22
|
+
raise ArgumentError, "Key #{key} undefined" unless OPTIONS.include?(key)
|
23
|
+
}
|
24
|
+
@description = options[:description]
|
25
|
+
@reference = options[:reference]
|
26
|
+
|
27
|
+
#TestCases. Key is TestCase name.
|
28
|
+
@tests = {}
|
29
|
+
|
30
|
+
end
|
31
|
+
#Key of the requirement
|
32
|
+
attr_reader :key
|
33
|
+
#Description of the requirement
|
34
|
+
attr_reader :description
|
35
|
+
#Reference, e.g. 'Page 47, Requirement document 2'
|
36
|
+
attr_reader :reference
|
37
|
+
#Logger. Defined by RequirementList#<<
|
38
|
+
attr_accessor :log
|
39
|
+
|
40
|
+
|
41
|
+
=begin rdoc
|
42
|
+
Returns the result of a TestCase.
|
43
|
+
|
44
|
+
* nil: no test found
|
45
|
+
* false: Not successfull
|
46
|
+
* true: Succesfull tested
|
47
|
+
=end
|
48
|
+
def [](key)
|
49
|
+
@tests[key]
|
50
|
+
end
|
51
|
+
|
52
|
+
=begin rdoc
|
53
|
+
Assign a TestCase.
|
54
|
+
=end
|
55
|
+
def test=(loc)
|
56
|
+
@log.info("Test #{loc} assigned to requirement #{@key}") if @log and @log.info?
|
57
|
+
@tests[loc] = false #Will be tested
|
58
|
+
end
|
59
|
+
|
60
|
+
=begin rdoc
|
61
|
+
Mark a test as successfull.
|
62
|
+
=end
|
63
|
+
def successfull_test(loc)
|
64
|
+
#fixme: Check if already defined?
|
65
|
+
#~ raise ArgumentError, "Successfull test #{loc} was never assigned" unless @tests.has_key?(loc)
|
66
|
+
@log.info("Test #{loc} successfull for to requirement #{@key}") if @log and @log.info?
|
67
|
+
@tests[loc] = true #Was successfull tested
|
68
|
+
end
|
69
|
+
=begin rdoc
|
70
|
+
Return false, if no test is defined.
|
71
|
+
Else you get the number of tests.
|
72
|
+
=end
|
73
|
+
def tested?()
|
74
|
+
return @tests.empty? ? false : @tests.size
|
75
|
+
end
|
76
|
+
=begin rdoc
|
77
|
+
Test, if the requirement was successfull.
|
78
|
+
|
79
|
+
If there is one test without success, the requirement is not successfull solved.
|
80
|
+
* nil: no test found
|
81
|
+
* false: At least one test not successfull
|
82
|
+
* true: Succesfull tested
|
83
|
+
=end
|
84
|
+
def successfull?()
|
85
|
+
return nil if @tests.empty?
|
86
|
+
success = true
|
87
|
+
@tests.each{|key,result|
|
88
|
+
success = (success and result)
|
89
|
+
}
|
90
|
+
return success
|
91
|
+
end
|
92
|
+
=begin rdoc
|
93
|
+
Return a list of tests with test result.
|
94
|
+
=end
|
95
|
+
def testresults()
|
96
|
+
#single tests: Only key returned
|
97
|
+
#~ return @tests.keys if @tests.size == 1
|
98
|
+
res_ok = []
|
99
|
+
res_err = []
|
100
|
+
@tests.each{|key, result|
|
101
|
+
#Convert key (for shoulda)
|
102
|
+
case key
|
103
|
+
when /^test: (.*) should (.*)\. \((.*)\)/
|
104
|
+
#old: test: _Shoulda should request_2. (Test_ShouldaTest)
|
105
|
+
#new: request_2 (_Shoulda/Test_ShouldaTest)
|
106
|
+
key = "#{$2} (#{$1}/#{$3})"
|
107
|
+
end
|
108
|
+
|
109
|
+
if result
|
110
|
+
res_ok << key
|
111
|
+
else
|
112
|
+
res_err << key
|
113
|
+
end
|
114
|
+
}
|
115
|
+
#return filled array, if other array is empty
|
116
|
+
return res_ok if res_err.empty?
|
117
|
+
return res_err if res_ok.empty?
|
118
|
+
|
119
|
+
[
|
120
|
+
"OK: #{res_ok.join(', ')}",
|
121
|
+
"Failure: #{res_err.join(', ')}",
|
122
|
+
]
|
123
|
+
end
|
124
|
+
|
125
|
+
def to_s()
|
126
|
+
"<Requirement #{key}>"
|
127
|
+
end
|
128
|
+
#~ def inspect()
|
129
|
+
#~ "<Requirement #{key} #{@tests}>"
|
130
|
+
#~ end
|
131
|
+
end
|
132
|
+
end #module Test4requirements
|
@@ -0,0 +1,163 @@
|
|
1
|
+
module Test4requirements
|
2
|
+
=begin rdoc
|
3
|
+
Define a list of requirements.
|
4
|
+
|
5
|
+
This class can be used in Test::Unit::TestCase#assign_requirement.
|
6
|
+
=end
|
7
|
+
class RequirementList
|
8
|
+
class << self
|
9
|
+
#alternatives: nil, :stdout, :txt
|
10
|
+
#fixme: check valid values. see #report_type=
|
11
|
+
attr_accessor :report_type_default
|
12
|
+
end
|
13
|
+
#Set default report type.
|
14
|
+
self.report_type_default = :stdout
|
15
|
+
=begin rdoc
|
16
|
+
Define a list of requirements.
|
17
|
+
|
18
|
+
Parameters will define the key of new Requirement, unless it is no Requirement.
|
19
|
+
=end
|
20
|
+
def initialize( *reqs )
|
21
|
+
|
22
|
+
@log = Log4r::Logger.new('ReqL')
|
23
|
+
|
24
|
+
@requirements = {}
|
25
|
+
reqs.each{|req|
|
26
|
+
|
27
|
+
#Define a requirement.
|
28
|
+
if ! req.is_a?(Requirement)
|
29
|
+
req = Requirement.new(req)
|
30
|
+
end
|
31
|
+
|
32
|
+
self << req
|
33
|
+
}
|
34
|
+
|
35
|
+
#Yes, we need two at_exit.
|
36
|
+
#tests are done also at_exit. With double at_exit, we are after that.
|
37
|
+
#Maybe better to be added later.
|
38
|
+
at_exit {
|
39
|
+
at_exit do
|
40
|
+
self.overview
|
41
|
+
end
|
42
|
+
}
|
43
|
+
@report_type = self.class.report_type_default # alternatives: nil, :stdout, :txt
|
44
|
+
end #initialize
|
45
|
+
#Logger
|
46
|
+
attr_reader :log
|
47
|
+
=begin rdoc
|
48
|
+
Add a new requirement.
|
49
|
+
=end
|
50
|
+
def <<(req)
|
51
|
+
raise ArgumentError, "Requirement is no Requiremnt but #{req.inspect}" unless req.is_a?(Requirement)
|
52
|
+
@log.info("Add requirement #{req.key}") if @log.info?
|
53
|
+
if @requirements[req.key]
|
54
|
+
raise ArgumentError, "Requirement #{req.key} defined twice"
|
55
|
+
end
|
56
|
+
req.log= Log4r::Logger.new("#{@log.name}::#{req.key}") unless req.log
|
57
|
+
@requirements[req.key] = req
|
58
|
+
end
|
59
|
+
=begin rdoc
|
60
|
+
Returns Requirement
|
61
|
+
=end
|
62
|
+
def [](key)
|
63
|
+
@requirements[key]
|
64
|
+
end
|
65
|
+
=begin rdoc
|
66
|
+
Assign a testcase to an requirement.
|
67
|
+
|
68
|
+
Requires:
|
69
|
+
* Requirement key
|
70
|
+
* testcase (Class#methodname)
|
71
|
+
=end
|
72
|
+
def assign_test(req, testcase)
|
73
|
+
raise ArgumentError unless @requirements[req]
|
74
|
+
@requirements[req].test= testcase
|
75
|
+
end
|
76
|
+
=begin rdoc
|
77
|
+
Report successfull test to the requirement.
|
78
|
+
|
79
|
+
This requires a previous assignment with #assign_test.
|
80
|
+
=end
|
81
|
+
def test_successfull(loc)
|
82
|
+
#~ raise ArgumentError unless @requirements[req]
|
83
|
+
@requirements.each{|key,req|
|
84
|
+
req.successfull_test(loc) unless req[loc].nil?
|
85
|
+
}
|
86
|
+
end
|
87
|
+
=begin rdoc
|
88
|
+
Defines, how and if an overview of the requirements are given.
|
89
|
+
|
90
|
+
Used in #overview. More details see there.
|
91
|
+
=end
|
92
|
+
def report_type=(key)
|
93
|
+
@log.info("Set report type #{key.inspect}") if @log.info?
|
94
|
+
case key
|
95
|
+
when :stdout, nil, :txt
|
96
|
+
@report_type = key
|
97
|
+
else
|
98
|
+
raise ArgumentError, "Unknown report type #{key} for #{self.class}"
|
99
|
+
end
|
100
|
+
end
|
101
|
+
=begin rdoc
|
102
|
+
Give an overview.
|
103
|
+
|
104
|
+
Returns a hash with test results for the requirements.
|
105
|
+
* true: Successfull tested
|
106
|
+
* false: Unsuccessfull tested
|
107
|
+
* nil: Not tested
|
108
|
+
|
109
|
+
The parameter defines alternative report types:
|
110
|
+
* nil: No output. Please evaluate the return value
|
111
|
+
* stdout: Result is written to stdout.
|
112
|
+
* :txt: The text of stdout as an Array
|
113
|
+
=end
|
114
|
+
def overview(report_type = @report_type )
|
115
|
+
|
116
|
+
txt = []
|
117
|
+
hash = {}
|
118
|
+
|
119
|
+
@requirements.each{|key, req|
|
120
|
+
if req.successfull?
|
121
|
+
txt << "Requirement #{key} was successfull tested (#{req.testresults.join(', ')})"
|
122
|
+
hash[key] = true
|
123
|
+
elsif req.tested?
|
124
|
+
txt << "Requirement #{key} was unsuccessfull tested (#{req.testresults.join(', ')})"
|
125
|
+
hash[key] = false
|
126
|
+
else
|
127
|
+
txt << "Requirement #{key} was not tested"
|
128
|
+
hash[key] = nil
|
129
|
+
end
|
130
|
+
}
|
131
|
+
|
132
|
+
case report_type
|
133
|
+
when :stdout
|
134
|
+
puts "Requirements overview:"
|
135
|
+
puts txt.join("\n")
|
136
|
+
when :txt
|
137
|
+
return txt
|
138
|
+
when nil
|
139
|
+
return hash
|
140
|
+
else
|
141
|
+
raise ArgumentError, "Undefined report type #{report_type}"
|
142
|
+
end
|
143
|
+
|
144
|
+
end #overview
|
145
|
+
=begin rdoc
|
146
|
+
Define an action at the end of the script (after the test).
|
147
|
+
|
148
|
+
This method can be used for user defined actions after test execution.
|
149
|
+
=end
|
150
|
+
def do_after_tests
|
151
|
+
raise ArgumentError, "do_after_tests: called without a block" unless block_given?
|
152
|
+
#Yes, we need two at_exit.
|
153
|
+
#The inner at_exit defines the action,
|
154
|
+
#the outer at_exit defines the action at the end.
|
155
|
+
#So it will be after the at_exit used by unit-test runner.
|
156
|
+
at_exit{
|
157
|
+
at_exit{
|
158
|
+
yield self
|
159
|
+
}
|
160
|
+
}
|
161
|
+
end #at_exit
|
162
|
+
end #RequirementList
|
163
|
+
end #module Test4requirements
|