Skip to content

Commit abf4236

Browse files
committed
Add shared test helpers
Extract shared assertions for Test::Unit and shared examples for RSpec to reduce duplicated expectations. Wire both suites to the shared helpers.
1 parent f93fc67 commit abf4236

5 files changed

Lines changed: 116 additions & 41 deletions

File tree

spec/spec_helper.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
require 'tree'
1313
require_relative 'support/fixtures'
14+
require_relative 'support/shared_examples'
1415

1516
if ENV['COVERAGE']
1617
begin

spec/support/shared_examples.rb

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# shared_examples.rb - This file is part of the RubyTree package.
2+
#
3+
# Copyright (c) 2026 Anupam Sengupta. All rights reserved.
4+
#
5+
# Redistribution and use in source and binary forms, with or without modification,
6+
# are permitted provided that the following conditions are met:
7+
#
8+
# - Redistributions of source code must retain the above copyright notice, this
9+
# list of conditions and the following disclaimer.
10+
#
11+
# - Redistributions in binary form must reproduce the above copyright notice, this
12+
# list of conditions and the following disclaimer in the documentation and/or
13+
# other materials provided with the distribution.
14+
#
15+
# - Neither the name of the organization nor the names of its contributors may
16+
# be used to endorse or promote products derived from this software without
17+
# specific prior written permission.
18+
#
19+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
23+
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24+
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25+
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26+
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29+
#
30+
# frozen_string_literal: true
31+
32+
RSpec.shared_examples 'detached node' do
33+
it 'does not equal "Object.new"' do
34+
expect(tree).not_to eq(Object.new)
35+
end
36+
37+
it 'does not equal 1 or any other fixnum' do
38+
expect(tree).not_to eq(1)
39+
end
40+
41+
it 'identifies itself as a root node' do
42+
expect(tree.root?).to be(true)
43+
end
44+
45+
it 'does not have a parent node' do
46+
expect(tree.parent).to be_nil
47+
end
48+
end
49+
50+
RSpec.shared_examples 'cloned node' do
51+
it 'is equal to the original' do
52+
expect(clone).to eq tree
53+
end
54+
55+
it 'is not identical to the original' do
56+
expect(clone).not_to be tree
57+
end
58+
end

spec/tree_spec.rb

Lines changed: 7 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,6 @@
1414
class SpecializedTreeNode < Tree::TreeNode; end
1515

1616
describe Tree do
17-
shared_examples_for 'any detached node' do
18-
it 'does not equal "Object.new"' do
19-
expect(tree).not_to eq(Object.new)
20-
end
21-
22-
it 'does not equal 1 or any other fixnum' do
23-
expect(tree).not_to eq(1)
24-
end
25-
26-
it 'identifies itself as a root node' do
27-
expect(tree.root?).to be(true)
28-
end
29-
30-
it 'does not have a parent node' do
31-
expect(tree.parent).to be_nil
32-
end
33-
end
3417

3518
describe '#initialize with empty name and nil content' do
3619
let(:tree) { Tree::TreeNode.new('') }
@@ -43,7 +26,7 @@ class SpecializedTreeNode < Tree::TreeNode; end
4326
expect(tree.content).to be_nil
4427
end
4528

46-
it_behaves_like 'any detached node'
29+
it_behaves_like 'detached node'
4730
end
4831

4932
describe "#initialize with name 'A' and nil content" do
@@ -57,7 +40,7 @@ class SpecializedTreeNode < Tree::TreeNode; end
5740
expect(tree.content).to be_nil
5841
end
5942

60-
it_behaves_like 'any detached node'
43+
it_behaves_like 'detached node'
6144
end
6245

6346
describe "#initialize with node name 'A' and some content" do
@@ -72,7 +55,7 @@ class SpecializedTreeNode < Tree::TreeNode; end
7255
expect(tree.content).to eq(sample)
7356
end
7457

75-
it_behaves_like 'any detached node'
58+
it_behaves_like 'detached node'
7659
end
7760

7861
describe 'comparison' do
@@ -117,21 +100,11 @@ class SpecializedTreeNode < Tree::TreeNode; end
117100
end
118101
end
119102

120-
shared_examples_for 'any cloned node' do
121-
it 'is equal to the original' do
122-
expect(clone).to eq tree
123-
end
124-
125-
it 'is not identical to the original' do
126-
expect(clone).not_to be tree
127-
end
128-
end
129-
130103
describe '#detached_copy', 'Without content' do
131104
let(:tree) { Tree::TreeNode.new('A', nil) }
132105
let(:clone) { tree.detached_copy }
133106

134-
it_behaves_like 'any cloned node'
107+
it_behaves_like 'cloned node'
135108
end
136109

137110
describe '#detached_copy with clonable content' do
@@ -146,7 +119,7 @@ class SpecializedTreeNode < Tree::TreeNode; end
146119
expect(clone.content).not_to be tree.content
147120
end
148121

149-
it_behaves_like 'any cloned node'
122+
it_behaves_like 'cloned node'
150123
end
151124

152125
describe '#detached_copy with unclonable content' do
@@ -157,7 +130,7 @@ class SpecializedTreeNode < Tree::TreeNode; end
157130
expect(clone.content).to be tree.content
158131
end
159132

160-
it_behaves_like 'any cloned node'
133+
it_behaves_like 'cloned node'
161134
end
162135

163136
describe '#detached_copy with false as content' do
@@ -168,6 +141,6 @@ class SpecializedTreeNode < Tree::TreeNode; end
168141
expect(clone.content).to be tree.content
169142
end
170143

171-
it_behaves_like 'any cloned node'
144+
it_behaves_like 'cloned node'
172145
end
173146
end

test/support/assertions.rb

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# assertions.rb - This file is part of the RubyTree package.
2+
#
3+
# Copyright (c) 2026 Anupam Sengupta. All rights reserved.
4+
#
5+
# Redistribution and use in source and binary forms, with or without modification,
6+
# are permitted provided that the following conditions are met:
7+
#
8+
# - Redistributions of source code must retain the above copyright notice, this
9+
# list of conditions and the following disclaimer.
10+
#
11+
# - Redistributions in binary form must reproduce the above copyright notice, this
12+
# list of conditions and the following disclaimer in the documentation and/or
13+
# other materials provided with the distribution.
14+
#
15+
# - Neither the name of the organization nor the names of its contributors may
16+
# be used to endorse or promote products derived from this software without
17+
# specific prior written permission.
18+
#
19+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
23+
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24+
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25+
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26+
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28+
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29+
#
30+
# frozen_string_literal: true
31+
32+
module TreeTestAssertions
33+
def assert_detached_node(node, name: nil, content_provided: false, content: nil)
34+
assert_not_nil(node, 'Node should not be nil')
35+
assert(node.root?, 'Node should identify as root')
36+
assert_nil(node.parent, 'Node should not have a parent')
37+
assert_equal(name, node.name, 'Node name does not match') if name
38+
assert_equal(content, node.content, 'Node content does not match') if content_provided
39+
end
40+
41+
def assert_clone_of(original, clone)
42+
assert_equal(original, clone, 'Clone should be equal to original')
43+
assert_not_same(original, clone, 'Clone should not be identical to original')
44+
end
45+
end

test/test_tree.rb

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,14 @@
3535
require 'json'
3636
require_relative '../lib/tree/tree_deps'
3737
require_relative 'support/fixtures_shared'
38+
require_relative 'support/assertions'
3839

3940
module TestTree
4041
# Test class for the Tree node.
4142
# noinspection RubyTooManyInstanceVariablesInspection
4243
class TestTreeNode < Test::Unit::TestCase
4344
include TreeTestFixtures
45+
include TreeTestAssertions
4446
Person = Struct.new(:First, :last) # A simple structure to use as the content for the nodes.
4547

4648
# Create this structure for the tests
@@ -96,13 +98,8 @@ def test_has_version_number
9698

9799
# This test is for the root alone - without any children being linked
98100
def test_root_setup
99-
assert_not_nil(@root, 'Root cannot be nil')
100-
assert_nil(@root.parent, 'Parent of root node should be nil')
101-
assert_not_nil(@root.name, 'Name should not be nil')
102-
assert_equal('ROOT', @root.name, "Name should be 'ROOT'")
103-
assert_equal('Root Node', @root.content, "Content should be 'Root Node'")
101+
assert_detached_node(@root, name: 'ROOT', content_provided: true, content: 'Root Node')
104102
assert(@root.to_s.include?('Content: Root Node'), 'to_s should include content value')
105-
assert(@root.root?, 'Should identify as root')
106103
assert(!@root.children?, 'Cannot have any children')
107104
assert(@root.content?, 'This root should have content')
108105
assert_equal(1, @root.size, 'Number of nodes should be one')
@@ -1205,14 +1202,15 @@ def test_detached_copy
12051202
assert(@root.children?, 'The root should have children')
12061203
copy_of_root = @root.detached_copy
12071204
assert(!copy_of_root.children?, 'The copy should not have children')
1208-
assert_equal(@root.name, copy_of_root.name, 'The names should be equal')
1205+
assert_clone_of(@root, copy_of_root)
12091206

12101207
# Try the same test with a child node
12111208
assert(!@child3.root?, 'Child 3 is not a root')
12121209
assert(@child3.children?, 'Child 3 has children')
12131210
copy_of_child3 = @child3.detached_copy
12141211
assert(copy_of_child3.root?, "Child 3's copy is a root")
12151212
assert(!copy_of_child3.children?, "Child 3's copy does not have children")
1213+
assert_clone_of(@child3, copy_of_child3)
12161214
end
12171215

12181216
# Test the detached_subtree_copy method.

0 commit comments

Comments
 (0)