Skip to content

Commit

Permalink
Add some tests for helpers (let)
Browse files Browse the repository at this point in the history
Use an unsafe, uninitialized value instead of initializing a `let` expression one extra time.
  • Loading branch information
icy-arctic-fox committed Dec 22, 2024
1 parent eb5cb4d commit 2c9c366
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 2 deletions.
67 changes: 67 additions & 0 deletions spec/spectator/core/helpers_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
require "../../spec_helper"

private class HelperObject
class_getter instances = [] of self

def initialize
@@instances << self
end
end

private def count_executed_examples(group)
count = 0
group.each_example do |example|
count += 1 if example.run?
end
count
end

Spectator.describe Spectator::Core::Helpers do
describe "#let" do
let x = 42

it "sets the value" do
expect(x).to eq(42)
end

let instance = HelperObject.new

it "sets the value before each example" do |example|
# There should be an instance created for each *executed* example in the group.
example_count = count_executed_examples(example.group)
expect(HelperObject.instances.size).to eq(example_count)
end

it "does not reuse the instance" do
expect(HelperObject.instances).to all_be_unique
end

it "retains the type and isn't nullable" do
# TODO: Use `have_type` matcher.
expect(typeof(x)).to eq(Int32)
# The type becomes a union due to the different type from the nested context.
expect(typeof(instance)).to eq(HelperObject | String)
end

let y = x + 1

it "can depend on other values" do
expect(y).to eq(43)
end

context "can override the value in a nested context" do
let x = 99

it "sets the value" do
expect(x).to eq(99)
end

let instance = "foo"

it "changes the type" do
# TODO: Use `have_type` matcher.
expect(typeof(instance)).to eq(String | HelperObject)
end
end
end
end
12 changes: 10 additions & 2 deletions src/spectator/core/helpers.cr
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
module Spectator::Core
def self.uninitialized_value_from_proc(proc : -> T) : T forall T
# The `uninitialized` keyword can't be used without assigning it to a variable.
# The compiler produces: `Error: undefined method 'uninitialized' for top-level`.
# This is a workaround.
uninitialized_value = uninitialized T
uninitialized_value
end

module Helpers
macro let(expr)
{{expr}}

%block = -> do
{{expr.value}}
end

{{expr.target}} = ::Spectator::Core.uninitialized_value_from_proc(%block)

before_each! do
{{expr.target}} = %block.call
end
Expand Down

0 comments on commit 2c9c366

Please # to comment.