test4requirements 0.1.0.alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|