Class: Tina4::Test

Inherits:
Object
  • Object
show all
Defined in:
lib/tina4/test.rb

Overview

Tina4 xUnit-style test base class — class-based test suites with HTTP helpers and positional assertions. Zero external dependencies.

Documentation chapter 18 has long described:

class UserApiTest < Tina4::Test
  def test_health
    resp = get("/health")
    assert_equal_value(resp.status, 200)
  end
end

Until 3.13.0 this class did not exist — examples crashed with “uninitialized constant Tina4::Test”. Ruby parity of the Python ‘tina4_python.test.Test` and PHP `Tina4Test` classes.

The class has a built-in runner (no Minitest/RSpec required):

results = Tina4::Test.run_all          # discovers all subclasses
# => { passed: 12, failed: 0, errors: 0, details: [...] }

Or run a single suite class:

UserApiTest.run!

HTTP helpers (get/post/put/patch/delete) delegate to TestClient. Positional assertions match the Python (actual, expected, message) shape used throughout the cross-framework docs.

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Class Attribute Details

.subclassesObject (readonly)

Returns the value of attribute subclasses.



42
43
44
# File 'lib/tina4/test.rb', line 42

def subclasses
  @subclasses
end

Class Method Details

.inherited(subclass) ⇒ Object



44
45
46
47
# File 'lib/tina4/test.rb', line 44

def inherited(subclass)
  super
  Test.subclasses << subclass
end

.run!Object

Run every test method (‘test_*`) on the calling subclass. Returns a hash with passed/failed/errors counts and per-test details.



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/tina4/test.rb', line 51

def run!
  instance_methods(false).grep(/\Atest_/).sort.each_with_object(
    { passed: 0, failed: 0, errors: 0, details: [] }
  ) do |method, results|
    suite = new
    begin
      suite.send(:set_up)
      suite.send(method)
      suite.send(:tear_down)
      results[:passed] += 1
      results[:details] << { suite: name, test: method, status: "passed" }
    rescue AssertionError => e
      results[:failed] += 1
      results[:details] << { suite: name, test: method, status: "failed", message: e.message }
    rescue StandardError => e
      results[:errors] += 1
      results[:details] << { suite: name, test: method, status: "error", message: "#{e.class}: #{e.message}" }
    end
  end
end

.run_all(quiet: false) ⇒ Object

Discover and run every Tina4::Test subclass.



73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/tina4/test.rb', line 73

def run_all(quiet: false)
  results = { passed: 0, failed: 0, errors: 0, details: [] }
  Test.subclasses.each do |klass|
    out = klass.run!
    results[:passed] += out[:passed]
    results[:failed] += out[:failed]
    results[:errors] += out[:errors]
    results[:details].concat(out[:details])
  end
  unless quiet
    puts "Tina4 Test results: #{results[:passed]} passed, #{results[:failed]} failed, #{results[:errors]} errors"
  end
  results
end

Instance Method Details

#assert_equal_value(actual, expected, message = nil) ⇒ Object

── Positional assertions — (actual, expected, message) shape ─────

Raises:



126
127
128
129
130
# File 'lib/tina4/test.rb', line 126

def assert_equal_value(actual, expected, message = nil)
  return if actual == expected

  raise AssertionError, message || "Expected #{expected.inspect}, got #{actual.inspect}"
end

#assert_false(value, message = nil) ⇒ Object

Raises:



144
145
146
147
148
# File 'lib/tina4/test.rb', line 144

def assert_false(value, message = nil)
  return unless value

  raise AssertionError, message || "Expected falsy, got #{value.inspect}"
end

#assert_nil_value(value, message = nil) ⇒ Object

Raises:



150
151
152
153
154
# File 'lib/tina4/test.rb', line 150

def assert_nil_value(value, message = nil)
  return if value.nil?

  raise AssertionError, message || "Expected nil, got #{value.inspect}"
end

#assert_not_equal_value(actual, expected, message = nil) ⇒ Object

Raises:



132
133
134
135
136
# File 'lib/tina4/test.rb', line 132

def assert_not_equal_value(actual, expected, message = nil)
  return unless actual == expected

  raise AssertionError, message || "Expected #{actual.inspect} != #{expected.inspect}, but they are equal"
end

#assert_not_nil_value(value, message = nil) ⇒ Object

Raises:



156
157
158
159
160
# File 'lib/tina4/test.rb', line 156

def assert_not_nil_value(value, message = nil)
  return unless value.nil?

  raise AssertionError, message || "Expected non-nil, got nil"
end

#assert_raises(expected_class, message = nil) ⇒ Object

Raises:

  • (ArgumentError)


162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/tina4/test.rb', line 162

def assert_raises(expected_class, message = nil)
  raise ArgumentError, "Block required" unless block_given?

  begin
    yield
  rescue StandardError => e
    return if e.is_a?(expected_class)

    raise AssertionError,
          message || "Expected #{expected_class}, got #{e.class}: #{e.message}"
  end
  raise AssertionError, message || "Expected #{expected_class} to be raised, but nothing was"
end

#assert_true(value, message = nil) ⇒ Object

Raises:



138
139
140
141
142
# File 'lib/tina4/test.rb', line 138

def assert_true(value, message = nil)
  return if value

  raise AssertionError, message || "Expected truthy, got #{value.inspect}"
end

#delete(path, headers: nil) ⇒ Object



120
121
122
# File 'lib/tina4/test.rb', line 120

def delete(path, headers: nil)
  test_client.delete(path, headers: headers)
end

#get(path, headers: nil) ⇒ Object



104
105
106
# File 'lib/tina4/test.rb', line 104

def get(path, headers: nil)
  test_client.get(path, headers: headers)
end

#patch(path, json: nil, body: nil, headers: nil) ⇒ Object



116
117
118
# File 'lib/tina4/test.rb', line 116

def patch(path, json: nil, body: nil, headers: nil)
  test_client.patch(path, json: json, body: body, headers: headers)
end

#post(path, json: nil, body: nil, headers: nil) ⇒ Object



108
109
110
# File 'lib/tina4/test.rb', line 108

def post(path, json: nil, body: nil, headers: nil)
  test_client.post(path, json: json, body: body, headers: headers)
end

#put(path, json: nil, body: nil, headers: nil) ⇒ Object



112
113
114
# File 'lib/tina4/test.rb', line 112

def put(path, json: nil, body: nil, headers: nil)
  test_client.put(path, json: json, body: body, headers: headers)
end

#set_upObject

── Lifecycle hooks ──────────────────────────────────────────────snake_case Tina4 idiom; override in subclasses.



92
93
# File 'lib/tina4/test.rb', line 92

def set_up
end

#tear_downObject



95
96
# File 'lib/tina4/test.rb', line 95

def tear_down
end

#test_clientObject

── HTTP test client (lazy) ───────────────────────────────────────



100
101
102
# File 'lib/tina4/test.rb', line 100

def test_client
  @test_client ||= Tina4::TestClient.new
end