### Concise expect-receive example Source: https://github.com/icy-arctic-fox/spectator/wiki/Stubs This example demonstrates the expect-receive syntax, which defines a stub and implicitly asserts its call by the end of the example. It's equivalent to using `allow` with `to_eventually`. ```crystal it "calls thing.call" do thing = double(:thing) expect(thing).to receive(:call).and_return(42) subject.doit(thing) end ``` -------------------------------- ### Spectator Context Hooks Example Source: https://github.com/icy-arctic-fox/spectator/blob/master/README.md Demonstrates the use of context hooks (before_all, after_all, before_each, after_each) for managing test setup and teardown, such as database initialization and cleanup. ```crystal # Initialize the database before running the tests in this context. before_all { Database.init } # Teardown the database and cleanup after tests in the is context finish. after_all { Database.cleanup } # Before each test, add some rows to the database. let(row_count) { 5 } before_each do row_count.times { Database.insert_row } end # Remove the rows after the test to get a clean slate. after_each { Database.clear } describe "#row_count" do it "returns the number of rows" do expect(Database.row_count).to eq(row_count) end end ``` -------------------------------- ### Run All Tests Source: https://github.com/icy-arctic-fox/spectator/blob/master/CONTRIBUTING.md Execute this command to verify the project setup and run all existing tests. ```bash crystal spec ``` -------------------------------- ### Adding Tags to Examples and Groups Source: https://github.com/icy-arctic-fox/spectator/wiki/Tags Demonstrates how to add simple symbol tags and key-value tags to individual examples or groups. ```crystal it "does a thing", :my_tag do # ... end it "does another thing", useful: "perhaps" do # ... end ``` -------------------------------- ### Basic Pre- and Post-condition Usage Source: https://github.com/icy-arctic-fox/spectator/wiki/Hooks Use pre_condition to check assumptions before an example runs and post_condition to verify that the state remains as expected after the example completes. This example ensures an array is not nil before the test and remains unmodified after. ```crystal let(original_array) { array.dup } pre_condition { expect(array).to_not be_nil } post_condition { expect(array).to match_array(original_array)} it "does something with the array" do # Test an operation on the array, # but the post_condition ensures the array wasn't modified. end ``` -------------------------------- ### Install Developer Dependencies Source: https://github.com/icy-arctic-fox/spectator/blob/master/CONTRIBUTING.md Run this command in the repository root after cloning to install necessary developer dependencies using Shards. ```bash shards ``` -------------------------------- ### TAP Output Example Source: https://github.com/icy-arctic-fox/spectator/wiki/Output This example demonstrates the output format adhering to the Test Anything Protocol (TAP). It lists test results sequentially with 'ok' or 'not ok' status. ```text 1..6 ok 1 - User age = 10 #can_drive? is false ok 2 - User age = 10 #can_vote? is false ok 3 - User age = 16 #can_drive? is true ok 4 - User age = 16 #can_vote? is false ok 5 - User age = 18 #can_drive? is true ok 6 - User age = 18 #can_vote? is true ``` -------------------------------- ### Using the Focus Tag Source: https://github.com/icy-arctic-fox/spectator/wiki/Tags Demonstrates the use of the `:focus` tag and its shortcut `fit` to run only specific examples. ```crystal it "does a thing", :focus do # ... end fit "does something else" do # ... end ``` -------------------------------- ### JSON Output Example Source: https://github.com/icy-arctic-fox/spectator/wiki/Output This is an example of the detailed JSON output format, which is not beautified by default. It includes example results, totals, and timing information. ```json { "examples": [ { "name": "String#size is the length of the string", "location": "test_helper_methods.cr:19", "result": "success", "time": 9.638e-06, "expectations": [ { "satisfied": true, "expected": "subject is length (using ==)", "actual": "subject is length (using ==)", "values": { "expected": "10", "actual": "10" } } ] } ], "totals": { "examples": 1, "success": 1, "fail": 0, "error": 0, "pending": 0, "remaining": 0 }, "timing": { "runtime": 7.53e-05, "examples": 9.638e-06, "overhead": 6.5662e-05 }, "result": "success" } ``` -------------------------------- ### Standard User Testing Example Source: https://github.com/icy-arctic-fox/spectator/wiki/Concise-Syntax This is a verbose example of testing user attributes like driving and voting eligibility using standard Spectator syntax. It demonstrates the repetition that `provided` aims to eliminate. ```crystal Spectator.describe User do subject(user) { User.new(age) } context "when 10" do let(age) { 10 } describe "#can_drive?" do subject { user.can_drive? } it "is false" do is_expected.to be_false end end describe "#can_vote?" do subject { user.can_vote? } it "is false" do is_expected.to be_false end end end context "when 16" do let(age) { 16 } describe "#can_drive?" do subject { user.can_drive? } it "is true" do is_expected.to be_true end end describe "#can_vote?" do subject { user.can_vote? } it "is false" do is_expected.to be_false end end end context "when 18" do let(age) { 18 } describe "#can_drive?" do subject { user.can_drive? } it "is true" do is_expected.to be_true end end describe "#can_vote?" do subject { user.can_vote? } it "is true" do is_expected.to be_true end end end end ``` -------------------------------- ### Enable Verbose Output Source: https://github.com/icy-arctic-fox/spectator/wiki/Command-Line-Usage Use the `-v` or `--verbose` option to list the names of examples and contexts as they are executed. ```text -v ``` ```text --verbose ``` -------------------------------- ### Failure Output Example (Dots Formatter) Source: https://github.com/icy-arctic-fox/spectator/wiki/Output This example shows the detailed output for a test failure when using the dots formatter. It includes the failure number, example name, reason, expected vs. actual values, and source location. ```text 1) Point#translate moves x negative <-- Failure number and example name Failure: x is less than 10 (using <) <-- Reason the expectation failed expected: >= 10 <-- Expected and actual values actual: 5 The values here depend on the matcher. # spec/point_spec.cr:25 <-- The source location of the example. ``` -------------------------------- ### Define an Example with a Label Source: https://github.com/icy-arctic-fox/spectator/blob/master/ARCHITECTURE.md This snippet demonstrates how to define an example with a descriptive label. Labels are captured to enhance the readability of test results and match data. ```crystal it "does something useful" do expect(the_answer).to eq(42) end ``` -------------------------------- ### Mocking and Doubles Example Source: https://github.com/icy-arctic-fox/spectator/blob/master/README.md Example demonstrating how to use mocks and doubles to test object interactions, including stubbing method calls and verifying received messages. ```crystal abstract class Interface abstract def invoke(thing) : String end # Type being tested. class Driver def do_something(interface : Interface, thing) interface.invoke(thing) end end Spectator.describe Driver do # Define a mock for Interface. mock Interface # Define a double that the interface will use. double(:my_double, foo: 42) it "does a thing" do # Create an instance of the mock interface. interface = mock(Interface) # Indicate that `#invoke` should return "test" when called. allow(interface).to receive(:invoke).and_return("test") # Create an instance of the double. dbl = double(:my_double) # Call the mock method. subject.do_something(interface, dbl) # Verify everything went okay. expect(interface).to have_received(:invoke).with(dbl) end end ``` -------------------------------- ### Spectator Hook and Condition Execution Order Source: https://github.com/icy-arctic-fox/spectator/wiki/Hooks Demonstrates the precise order of execution for Spectator hooks and conditions in a nested describe block. This example highlights the flow from outer to inner blocks and the sequence of setup, execution, and teardown phases. ```crystal Spectator.describe String do before_all { puts "outer before_all" } # 1 before_each { puts "outer before_each" } # 5 after_all { puts "outer after_all" } # 17 after_each { puts "outer after_each" } # 13 around_each do |proc| puts "outer around_each begin" # 3 proc.call puts "outer around_each end" # 15 end pre_condition { puts "outer pre_condition" } # 7 post_condition { puts "outer post_condition" } # 11 describe "#foo" do before_all { puts "inner before_all" } # 2 before_each { puts "inner before_each" } # 6 after_all { puts "inner after_all" } # 16 after_each { puts "inner after_each" } # 12 around_each do |proc| puts "inner around_each begin" # 4 proc.call puts "inner around_each end" # 14 end pre_condition { puts "inner pre_condition" } # 8 post_condition { puts "inner post_condition" } # 10 it "does something" do puts "example code" # 9 end end end ``` -------------------------------- ### Filter Tests by Example Name Source: https://github.com/icy-arctic-fox/spectator/wiki/Command-Line-Usage Use the `-e STRING` or `--example STRING` option to run a specific test by its full name. ```text -e STRING ``` ```text --example STRING ``` -------------------------------- ### Identical Expect and Should Syntax Examples Source: https://github.com/icy-arctic-fox/spectator/wiki/Should Demonstrates that 'expect' and 'should' syntax provide identical functionality for basic assertions and proc expectations. ```crystal expect("foo").to match(/foo/) "foo".should match(/foo/) ``` ```crystal expect { raise "oops" }.to raise_error(/oops/) ->{ raise "oops" }.should raise_error(/oops/) ``` -------------------------------- ### Using Skip and Pending Tags Source: https://github.com/icy-arctic-fox/spectator/wiki/Tags Illustrates how to use the `:skip` and `pending:` tags to mark examples as skipped, optionally with a reason. ```crystal it "doesn't do a thing", :skip do # ... end it "will eventually do something", pending: "Not implemented yet!" do # ... end ``` -------------------------------- ### Concise User Testing with `provided` Source: https://github.com/icy-arctic-fox/spectator/wiki/Concise-Syntax This example demonstrates the `provided` syntax to test user eligibility, significantly reducing verbosity and repetition compared to the standard approach. Each `provided` block defines a distinct test case with specific inputs. ```crystal subject(user) { User.new(age) } provided age: 10 do expect(user.can_drive?).to be_false expect(user.can_vote?).to be_false end provided age: 16 do expect(user.can_drive?).to be_true expect(user.can_vote?).to be_false end provided age: 18 do expect(user.can_drive?).to be_true expect(user.can_vote?).to be_true end ``` -------------------------------- ### Setup and Teardown with before_each and after_each Source: https://github.com/icy-arctic-fox/spectator/wiki/Hooks Use `before_each` to set up test environments and `after_each` to clean them up. These hooks run before and after every test, respectively, within the same context. ```crystal before_each { DB.populate_with_sample_data } # 1 after_each { DB.empty } it "does something" do # 2 end ``` -------------------------------- ### Accessing Tags at Runtime Source: https://github.com/icy-arctic-fox/spectator/wiki/Tags Shows how to access tag values within an example's metadata during runtime. ```crystal it "does a thing", answer: "foobar" do |example| expect(example.metadata[:answer]).to eq("foobar") end ``` -------------------------------- ### Tag Inheritance and Overriding Source: https://github.com/icy-arctic-fox/spectator/wiki/Tags Shows how examples inherit tags from parent groups and how tags can be overridden or removed. ```crystal describe "awesome feature", :tag1 do it "does a thing", :tag2 do |example| expect(example.tags).to eq([:tag1, :tag2]) end it "does another thing", :tag3, tag1: false do |example| expect(example.tags).to eq([:tag3]) end end ``` -------------------------------- ### Detailed Spectator Configuration in spec_helper.cr Source: https://github.com/icy-arctic-fox/spectator/wiki/Configuration This comprehensive example demonstrates various configuration methods available within the `Spectator.configure` block in `spec_helper.cr`, including fail-fast, randomization, hooks, filters, and formatters. ```crystal Spectator.configure do |config| config.fail_fast # Enables fail-fast mode. config.fail_fast = true # Enables or disables fail-fast mode. config.fail_blank # Enables fail-blank mode. config.fail_blank = true # Enables or disables fail-blank mode. config.dry_run # Enables dry-run mode. config.dry_run = true # Enables or disables dry-run mode. config.seed = 12345 # Sets the seed used for the random number generator. config.randomize # Randomizes test execution order. config.randomize = true # Enables or disables running tests in a random order. config.profile # Displays profiling information. config.profile = true # Enables or disables displaying profiling information. config.before_suite do # Runs a block of code before the test suite. end config.after_suite do # Runs a block of code after the test suite. end config.before_all do # Runs a block of code before all top-level group. end config.after_all do # Runs a block of code after each top-level group. end config.before_each do |example| # Runs a block of code before each example. end config.after_each do |example| # Runs a block of code after each example. end config.around_each do |example| example.call end config.add_node_filter(filter) # Adds a test filter. config.formatter = formatter # Sets the primary output formatter. config.add_formatter(formatter) # Adds a secondary output formatter. # Specifies one or more tags to constrain running examples to. config.filter_run_including :tag, tag2: "value" # Specifies one or more tags to exclude from running examples. config.filter_run_excluding :tag, tag2: "value" # Specifies one or more tags to filter on only if they're present in the spec. config.filter_run_when_matching :focus end ``` -------------------------------- ### Test Execution within an Example Source: https://github.com/icy-arctic-fox/spectator/blob/master/ARCHITECTURE.md Illustrates the structure of an example in Spectator, highlighting the 'test code' block. This code is executed during a test run, and assertions like `expect(the_answer).to eq(42)` are made within this block. ```crystal it "does a thing" do # Test code starts here. Everything inside this `it` block is considered a test. expect(the_answer).to eq(42) # Test code ends here. end ``` -------------------------------- ### Helper Method for Stubbing Source: https://github.com/icy-arctic-fox/spectator/wiki/Stubs This example demonstrates defining a helper method to encapsulate a reusable stub configuration, making tests cleaner. ```crystal double :time_double, time_in: Time.utc(2016, 2, 15, 10, 20, 30) def receive_time_in_utc receive(:time_in).with(:utc).and_return(Time.utc) end it "returns the time in UTC" do dbl = double(:time_double) allow(dbl).to receive_time_in_utc expect(dbl.time_in(:utc).zone.name).to eq("UTC") end ``` -------------------------------- ### Nesting Contexts with Hooks Source: https://github.com/icy-arctic-fox/spectator/wiki/Structure Shows how to deeply nest contexts to build complex scenarios and use `before_each` and `after_each` hooks for setup and teardown within different levels of the test structure. ```crystal require "./spec_helper" Spectator.describe LoginPage do let(username) { 'bob' } let(password) { 'password' } before_each { create_user(username, password) } after_each { delete_user(username) } context "when 'Remember me' is checked" do before_each { subject.remember_me = true } # ... end context "when given a valid username" do before_each { subject.username = username } context "when given a correct password" do before_each { subject.password = password } # ... end context "when given an incorrect password" do before_each { subject.password = 'incorrect' } # ... end end context "when given an invalid username" do before_each { subject.username = 'jim' } # ... end end ``` -------------------------------- ### Example of Compound Matcher Usage Source: https://github.com/icy-arctic-fox/spectator/wiki/Compound-Matchers This example demonstrates how compound matchers could be used to chain multiple assertions together. Compound matchers are not currently supported in Spectator. ```crystal expect("foobar").to start_with("foo").and end_with("bar") ``` -------------------------------- ### Configure spec/spec_helper.cr Source: https://github.com/icy-arctic-fox/spectator/wiki/Getting-Started Create or update the spec/spec_helper.cr file to require Spectator and your shard's source code. This setup is necessary before writing any tests. ```crystal require "../src/*" require "spectator" ``` -------------------------------- ### Format Code Source: https://github.com/icy-arctic-fox/spectator/blob/master/CONTRIBUTING.md Use this command to automatically format your code according to Crystal's standard style guide before committing. ```bash crystal tool format ``` -------------------------------- ### Expect-Receive with deferred expectation Source: https://github.com/icy-arctic-fox/spectator/wiki/Stubs This shows how `expect-receive` is functionally similar to using `allow` with `to_eventually`, ensuring the stub is called before the example concludes. ```crystal it "calls thing.call" do thing = double(:thing) allow(thing).to receive(:call).and_return(42) expect(thing).to_eventually have_received(:call) subject.doit(thing) end ``` -------------------------------- ### Spectator User Model Test Setup Source: https://github.com/icy-arctic-fox/spectator/wiki/Subject Sets up tests for a `MyShard::Models::User` class. Defines common lets and a general user subject, then uses nested describes to test specific methods like `#name`, `#first_name`, and `#last_name`. ```ruby Spectator.describe MyShard::Models::User do # Values and general-purpose subject for all tests that need it. let(first_name) { "Bob" } let(last_name) { "Ross" } subject(user) { described_class.new(first_name: first_name, last_name: last_name) } describe "#name" do # Narrow scope of subject to just the method being tested. subject { user.name } it "is the first and last name" do is_expected.to start_with(first_name) is_expected.to end_with(last_name) end end describe "#first_name" do # Redefining the first name and subject for this context. let(first_name) { "Joe" } subject { user.first_name } it "is the expected value" do is_expected.to eq(first_name) end end describe "#last_name" do let(last_name) { "Smith" } subject { user.last_name } it "is the expected value" do is_expected.to eq(last_name) end end end ``` -------------------------------- ### Write a Minimal Spectator Spec Source: https://github.com/icy-arctic-fox/spectator/wiki/Getting-Started A basic example demonstrating how to structure a test suite using Spectator. It shows the use of `Spectator.describe`, `subject`, `describe`, `context`, `let`, and `it` blocks for defining tests. ```crystal require "./spec_helper" Spectator.describe String do subject { "foo" } describe "#==" do context "with the same value" do let(value) { subject.dup } it "is true" do is_expected.to eq(value) end end context "with a different value" do let(value) { "bar" } it "is false" do is_expected.to_not eq(value) end end end end ``` -------------------------------- ### Check String Starts With Value Source: https://github.com/icy-arctic-fox/spectator/wiki/String-Matchers Use `start_with` to verify if a string begins with a specified substring, character, or matches a regular expression. Case-insensitive regex matching is supported. ```crystal expect("foobar").to start_with("foo") # true expect("foobar").to start_with('f') # true expect("FOOBAR").to start_with(/foo/i) # true expect("foobar").to start_with("bar") # false expect("foobar").to start_with('b') # false expect("FOOBAR").to start_with(/bar/i) # false ``` -------------------------------- ### Check if Collection Starts with a Value Source: https://github.com/icy-arctic-fox/spectator/wiki/Collection-Matchers Use `start_with` to verify if the first element of a collection matches a given criteria. The criteria can be a value, type, or regular expression. ```crystal expect([1, 2, 3]).to start_with(1) # true expect(%i[a b c]).to start_with(Symbol) # true expect(%w(foo bar)).to start_with(/foo/) # true expect([1, 2, 3]).to start_with(3) # false expect({:a, 2, "foo"}).to start_with(Int32) # false ``` -------------------------------- ### Basic around_each Usage Source: https://github.com/icy-arctic-fox/spectator/wiki/Hooks Demonstrates the fundamental usage of around_each to wrap an example within a block. The proc must be called to execute the test. ```crystal around_each do |proc| Thing.run do proc.call end end it "does something" do # ... end ``` -------------------------------- ### Bowling Pin Count Example Source: https://github.com/icy-arctic-fox/spectator/wiki/Expression-Labels Demonstrates a basic scenario for tracking bowling pins. This code sets up a pin count and simulates knocking down pins. ```crystal pins = Pins.new # Start with 10 pins. pins.knock_down([1, 2, 3, 4, 5, 6, 8, 9]) # Knock down all but 7 and 10. expect(pins.remaining).to eq(2) ``` -------------------------------- ### Expect a Raised Error Source: https://github.com/icy-arctic-fox/spectator/wiki/Error-Matchers A basic example demonstrating how to use the raise_error matcher to check if a block of code raises any error. ```crystal expect { raise "oops" }.to raise_error # true ``` -------------------------------- ### around_each with Missing proc.call Source: https://github.com/icy-arctic-fox/spectator/wiki/Hooks Illustrates a common mistake where the proc is not called within the around_each block, resulting in the example never running. ```crystal around_each do |proc| Thing.run do # Missing proc.call end end it "does something" do # Whoops! This is never run. end ``` -------------------------------- ### Verifying Method Call with Arguments on Mock Target Source: https://github.com/icy-arctic-fox/spectator/wiki/Mocks Extends the previous example to verify that the `call` method on the mock `Target` was invoked with a specific argument (42). ```crystal it "invokes #call on the target" do target = mock(Target) subject.emit(target) expect(target).to have_received(:call).with(42) end ``` -------------------------------- ### Matcher Example: Expecting a specific value Source: https://github.com/icy-arctic-fox/spectator/blob/master/ARCHITECTURE.md Demonstrates the basic usage of a matcher to assert an expected value in a test. The `eq(42)` creates a matcher that checks if the actual value is equal to 42. ```crystal expect(the_answer).to eq(42) ``` -------------------------------- ### Assert specific initial and final values with 'change' Source: https://github.com/icy-arctic-fox/spectator/wiki/Other-Matchers Verify that an expression changes from a specific starting value to a specific ending value using '.from(BEFORE).to(AFTER)'. ```crystal i = 0 expect { i += 1 }.to change { i }.from(0).to(1) # true ``` -------------------------------- ### Sequential Pre-condition Execution Source: https://github.com/icy-arctic-fox/spectator/wiki/Hooks When multiple pre_condition blocks are defined, they are executed in the order they appear in the code. This allows for a series of checks to be performed before the example runs. ```crystal pre_condition { expect(array).to_not be_nil } # 1 pre_condition { expect(array.size).to eq(3) } # 2 ``` -------------------------------- ### Verify Method Call with Arguments Source: https://github.com/icy-arctic-fox/spectator/wiki/Doubles Verify that a method was called on a double with specific arguments. This example checks if `call` was invoked with the argument `42`. ```crystal it "invokes #call on the target" do target = double(:target) subject.emit(target) expect(target).to have_received(:call).with(42) end ``` -------------------------------- ### Run Ameba Linter Source: https://github.com/icy-arctic-fox/spectator/blob/master/CONTRIBUTING.md Execute Ameba to check for best coding practices and potential issues in the codebase. This requires shards to be installed. ```bash bin/ameba ``` -------------------------------- ### around_each with Other Hooks Source: https://github.com/icy-arctic-fox/spectator/wiki/Hooks Shows the execution order of around_each when mixed with before_all, after_all, before_each, and after_each hooks. around_each starts before before_each and finishes after after_each. ```crystal describe Something do before_all { DB.setup } # 1 after_all { DB.cleanup } # 7 around_each do |proc| Simulation.run do # 3 proc.call end # 5 end describe "#foo" do before_each { DB.start_transaction } # 2 after_each { DB.end_transaction } # 6 it "does a cool thing" do # 4 end end end ``` -------------------------------- ### Basic before_all and after_all Usage Source: https://github.com/icy-arctic-fox/spectator/wiki/Hooks Demonstrates the basic syntax for `before_all` and `after_all` hooks. These run once before and after all tests in the current context, respectively. ```crystal before_all { DB.populate_with_sample_data } # 1 after_all { DB.empty } it "does something" do # 2 end ``` -------------------------------- ### Basic Spectator Spec Structure Source: https://github.com/icy-arctic-fox/spectator/wiki/Structure A fundamental Spectator spec file demonstrating the top-level context, nested describe block, subject declaration, and a basic example with an assertion. ```crystal require "./spec_helper" # Top-level context/example group. Spectator.define MyCoolClass do # Nested context/example group. describe "#do_something" do # Subject desclaration. subject { described_class.do_something } # Example. it "does something" do # Assertion. is_expected.to_not be_nil end end end ``` -------------------------------- ### Cached and Lazy Subject Initialization Source: https://github.com/icy-arctic-fox/spectator/wiki/Subject Demonstrates that the subject is cached and lazily initialized. The same instance is used throughout an example, and it's only created upon first reference. ```crystal subject { Time.utc } it "uses the same instance" do now = subject sleep(5) expect(subject).to eq(now) # subject is not re-evaluated. end ``` -------------------------------- ### Using `sample` with a Helper Method Source: https://github.com/icy-arctic-fox/spectator/wiki/Sample-Values Shows how to use a helper method that returns a collection for the `sample` context. The `integer` block argument receives each value. ```crystal def integers [-1, 0, 1, 21] end sample integers do |integer| it "accepts the value" do values.add(integer) expect(values).to end_with(integer) end end ``` -------------------------------- ### Lazy Evaluation and Caching with `let` Source: https://github.com/icy-arctic-fox/spectator/wiki/Subject Demonstrates that `let` evaluates its expression lazily on first reference and caches the result for subsequent calls within the same test. The example shows that the cached time value remains the same even after a delay. ```crystal let(current_time) { Time.utc } it "lazy evaluates" do now = current_time sleep 5 expect(current_time).to eq(now) end ``` -------------------------------- ### Example of a Yield Matcher in Spectator (Conceptual) Source: https://github.com/icy-arctic-fox/spectator/wiki/Yield-Matchers This is a conceptual example of how a yield matcher might be implemented in Spectator, inspired by RSpec. It shows how to expect a block to yield a specific value. ```crystal expect { |b| yielding_method(&b) }.to yield_value("foo") ``` -------------------------------- ### Configuration Options in .spectator file Source: https://github.com/icy-arctic-fox/spectator/wiki/Configuration Place these command-line options in a `.spectator` file in your project root to automatically configure Spectator. ```text --fail-blank --rand --profile ``` -------------------------------- ### Crystal 'be_nil' Matcher Example Source: https://github.com/icy-arctic-fox/spectator/wiki/Truthy-Matchers Use the 'be_nil' matcher to verify if a value is exactly 'nil'. ```crystal expect(true).to be_nil # false expect(5).to be_nil # false expect(false).to be_nil # false expect(nil).to be_nil # true ``` -------------------------------- ### Reference Subject in Test Source: https://github.com/icy-arctic-fox/spectator/wiki/Subject References the previously defined subject within a test example to assert its properties. ```crystal it "has the expected name" do expect(subject.name).to be("Bob") end ``` -------------------------------- ### Crystal 'be_falsey' Matcher Example Source: https://github.com/icy-arctic-fox/spectator/wiki/Truthy-Matchers Use the 'be_falsey' matcher to assert that a value is either 'false' or 'nil'. ```crystal expect(true).to be_falsey # false expect(5).to be_falsey # false expect(false).to be_falsey # true expect(nil).to be_falsey # true ``` -------------------------------- ### Alternative `match` Usages (with recommendations) Source: https://github.com/icy-arctic-fox/spectator/wiki/Equality-Matchers Demonstrates alternative usages of `match` that have more specific recommended matchers. For simple string equality, use `eq`. For type checking, use `be_a`. ```crystal expect("foo").to match("foo") expect("foo").to eq("foo") # recommended expect(5).to match(Int32) expect(5).to be_a(Int32) # recommended ``` -------------------------------- ### Crystal 'be_truthy' Matcher Example Source: https://github.com/icy-arctic-fox/spectator/wiki/Truthy-Matchers Use the 'be_truthy' matcher to assert that a value is neither 'false' nor 'nil'. ```crystal expect(true).to be_truthy # true expect(5).to be_truthy # true expect(false).to be_truthy # false expect(nil).to be_truthy # false ``` -------------------------------- ### Multiple before_all Hooks Source: https://github.com/icy-arctic-fox/spectator/wiki/Hooks Shows how multiple `before_all` hooks are executed in the order they are defined within the same context. ```crystal before_all { Thing.first } # 1 before_all { Thing.second } # 2 ``` -------------------------------- ### Crystal 'be' Matcher Example Source: https://github.com/icy-arctic-fox/spectator/wiki/Truthy-Matchers Use the 'be' matcher to test if a value is truthy. It is a shorthand for 'be_truthy'. ```crystal expect(true).to be # true expect(5).to be # true expect(false).to be # false expect(nil).to be # false ``` -------------------------------- ### Define a User Subject Source: https://github.com/icy-arctic-fox/spectator/wiki/Subject Defines the subject of the test as a new User instance. This subject can then be referenced in test examples. ```crystal subject { User.new } ``` -------------------------------- ### Crystal 'be_false' Matcher Example Source: https://github.com/icy-arctic-fox/spectator/wiki/Truthy-Matchers Use the 'be_false' matcher to check if a value is strictly equal to the boolean 'false'. ```crystal expect(true).to be_false # false expect(5).to be_false # false expect(false).to be_false # true expect(nil).to be_false # false ``` -------------------------------- ### Crystal 'be_true' Matcher Example Source: https://github.com/icy-arctic-fox/spectator/wiki/Truthy-Matchers Use the 'be_true' matcher to specifically check if a value is equal to the boolean 'true'. ```crystal expect(true).to be_true # true expect(5).to be_true # false expect(false).to be_true # false expect(nil).to be_true # false ``` -------------------------------- ### Class Mock with Keyword Argument Stub Source: https://github.com/icy-arctic-fox/spectator/wiki/Mocks Illustrates creating a class mock using `class_mock` and defining a stub for a class method (`something`) using keyword arguments, directly setting the return value. ```crystal class MyClass def self.something 0 end end mock MyClass do # Define class methods with `self.` prefix. stub def self.something 42 end end it "does something" do # Default stubs can be defined with key-value pairs (keyword arguments). mock = class_mock(MyClass, something: 3) expect(mock.something).to eq(3) # Stubs can be changed with `allow`. allow(mock).to receive(:something).and_return(5) expect(mock.something).to eq(5) # Even the expect-receive syntax works. expect(mock).to receive(:something).and_return(7) mock.something end ``` -------------------------------- ### Basic Configuration in spec_helper.cr Source: https://github.com/icy-arctic-fox/spectator/wiki/Configuration Use this snippet in `spec_helper.cr` to enable common Spectator features like fail-blank, randomization, and profiling. ```crystal Spectator.configure do |config| config.fail_blank config.randomize config.profile end ``` -------------------------------- ### Using `sample` without a Block Argument Source: https://github.com/icy-arctic-fox/spectator/wiki/Sample-Values Demonstrates using the `sample` context when the block argument is omitted. The `value` keyword can be used instead to access the current item from the collection. ```crystal def lots_of_integers -100..100 end sample lots_of_integers, count: 5 do it "accepts the value" do values.add(value) expect(values).to end_with(value) end end ``` -------------------------------- ### Mocking a Module for Inclusion Source: https://github.com/icy-arctic-fox/spectator/wiki/Mock-Modules Shows how to mock a module and instantiate a class that includes it. This is useful for testing module inclusions. ```crystal module Runnable def run # ... end end mock Runnable specify do runnable = mock(Runnable) # or new_mock(Runnable) runnable.run end ``` -------------------------------- ### Accessing Context in before_each Hook Source: https://github.com/icy-arctic-fox/spectator/wiki/Hooks Hooks can access and modify variables defined in the same scope. This example shows a `before_each` hook appending to an array. ```crystal let(array) { [1, 2, 3] } before_each { array << 4 } ``` -------------------------------- ### Mixed 'each' and 'all' Hooks Execution Order Source: https://github.com/icy-arctic-fox/spectator/wiki/Hooks Illustrates the execution order when `before_each`, `after_each`, `before_all`, and `after_all` hooks are used together. `before_all` runs first, and `after_all` runs last. ```crystal describe Something do before_all { DB.setup } # 1 after_all { DB.cleanup } # 7 before_each { DB.start_transaction } # 3 after_each { DB.abort_transaction } # 6 describe "#foo" do before_each { DB.add_sample_data } # 2 after_each { DB.remove_sample_data } # 5 it "does a cool thing" do # 4 end end end ``` -------------------------------- ### Using describe and context for Grouping Source: https://github.com/icy-arctic-fox/spectator/wiki/Structure Illustrates the interchangeable use of `describe` and `context` for grouping tests. `describe` is typically used for types, methods, or return values, while `context` explains specific situations or scenarios. ```crystal require "./spec_helper" # describe is used because we're describing a type (String). Spectator.describe String do # context is used because we're explaining a state. context "when empty" do # describe is used because we're describing a method (#empty?). describe "#empty?" do it "is true" do # ... end end end # Different situation. context "when not empty" do describe "#empty?" do it "is false" do # ... end end end end ``` -------------------------------- ### Immediate Evaluation and Caching with `let!` Source: https://github.com/icy-arctic-fox/spectator/wiki/Subject Shows that `let!` initializes its expression immediately before the test runs. The value is cached and reused throughout the test, as demonstrated by comparing the cached time with a newly generated time after a delay. ```crystal let!(current_time) { Time.utc } it "evaluates immediately" do sleep 5 expect(current_time).to_not eq(Time.utc) end ``` -------------------------------- ### Class with Parameterized Initializer Source: https://github.com/icy-arctic-fox/spectator/wiki/Mocks Example of a Crystal class that requires a parameter during initialization. This type of class cannot be directly mocked by `new_mock` without special handling. ```crystal class MyClass def initialize(@value : Int32) end end ``` -------------------------------- ### Basic Matcher Usage in Crystal Source: https://github.com/icy-arctic-fox/spectator/wiki/Matchers Demonstrates the fundamental usage of a matcher with the `expect` method in Crystal. The matcher is appended after the `#to` method. ```crystal expect(foo).to eq(bar) # ^^^^^^^ This is the matcher. ``` -------------------------------- ### Include Helper Methods via Module Mixin Source: https://github.com/icy-arctic-fox/spectator/wiki/Helper-Methods Helper methods can be organized into modules and included into Spectator contexts using `include`. This promotes modularity and reusability. ```crystal module StringHelpers def random_string chars = ('a'..'z').to_a String.build(length) do |builder| length.times { builder << chars.sample } end end end Spectator.describe String do include StringHelpers describe "#size" do let(length) { 10 } subject { random_string.size } it "is the length of the string" do is_expected.to eq(length) end end end ``` -------------------------------- ### Equivalent Negated Assertion Syntaxes Source: https://github.com/icy-arctic-fox/spectator/wiki/Subject Illustrates various equivalent syntaxes for negated assertions using subjects, including `expect`, `is_expected`, `should_not`, and `is_not`. ```crystal expect(subject).to_not eq(42) is_expected.to_not eq(42) should_not eq(42) is_not(42) ``` -------------------------------- ### before_all Hook Limitation Source: https://github.com/icy-arctic-fox/spectator/wiki/Hooks Illustrates that `before_all` hooks cannot modify or access context variables defined with `let`. The hook runs in a different scope than the test examples. ```crystal let(array) { [1, 2, 3] } before_all { array << 4 } # DOES NOT WORK! ``` -------------------------------- ### Define Default Stubs with Keyword Arguments Source: https://github.com/icy-arctic-fox/spectator/wiki/Mocks Quickly define default return values for methods using keyword arguments. These stubs are generally static and respond to any call with the same method name. ```crystal mock MyClass, answer: 42, foo: "bar" ``` -------------------------------- ### Basic Output Matcher Source: https://github.com/icy-arctic-fox/spectator/wiki/Output-Matchers Asserts that a block of code produces the exact string 'foo' to standard output. This functionality is not yet implemented. ```crystal expect { write_stuff }.to output("foo") ``` -------------------------------- ### Mocking a Class with Parameterized Initializer Source: https://github.com/icy-arctic-fox/spectator/wiki/Mocks Demonstrates how to define a parameterless `initialize` method within a mock's body to allow `new_mock` to create instances of classes that normally require initialization parameters. The instance variable is set to a default value. ```crystal class MyClass def initialize(@value : Int32) end end mock MyClass do def initialize(@value : Int32 = 0) # Note the lack of `stub` here. end end it "can create a mock" do mock = mock(MyClass) expect(mock).to_not be_nil end ``` -------------------------------- ### Using `sample` with a Literal Array Source: https://github.com/icy-arctic-fox/spectator/wiki/Sample-Values Demonstrates repeating tests for each integer in a literal array. The block argument `integer` receives each value from the collection. ```crystal sample [-1, 0, 1, 42] do |integer| it "accepts the value" do values.add(integer) expect(values).to end_with(integer) end end ``` -------------------------------- ### Instantiate a Double with `new_double` Source: https://github.com/icy-arctic-fox/spectator/wiki/Doubles An alternative syntax to `double` for instantiating a double, `new_double` can be used for clarity. ```crystal it "does something" do dbl = new_double(:my_double) end ``` -------------------------------- ### Instantiate a Mock with `new_mock` Source: https://github.com/icy-arctic-fox/spectator/wiki/Mocks Alternatively, use `new_mock` to create an instance of a mock object. This functions similarly to `mock` for instantiation purposes. ```crystal it "does something" do mock = new_mock(MyClass) end ``` -------------------------------- ### Define and Reuse a String with `let` Source: https://github.com/icy-arctic-fox/spectator/wiki/Subject Use `let` to define a named expression that can be reused across multiple examples within the same test context. The value is lazily evaluated and cached. ```crystal let(string) { "foobar" } it "isn\'t empty" do expect(string.empty?).to be_false end it "is six characters" do expect(string.size).to eq(6) end ``` -------------------------------- ### Filter Tests by Tag Source: https://github.com/icy-arctic-fox/spectator/wiki/Command-Line-Usage Use the `--tag TAG[:VALUE]` option to include tests with specific tags. Prepend a `~` to exclude a tag, for example, `--tag ~slow`. ```text --tag TAG[:VALUE] ``` ```text --tag ~slow ``` -------------------------------- ### Using `sample` with a Count Limit Source: https://github.com/icy-arctic-fox/spectator/wiki/Sample-Values Illustrates limiting the number of iterations for a `sample` context using the `count` option. The `integer` block argument receives each selected value. ```crystal def lots_of_integers -100..100 end sample lots_of_integers, count: 5 do |integer| it "accepts the value" do values.add(integer) expect(values).to end_with(integer) end end ``` -------------------------------- ### Injecting Mock into an Existing Module Source: https://github.com/icy-arctic-fox/spectator/wiki/Mock-Modules Demonstrates how to inject mock functionality into an existing module using `inject_mock`. This approach is generally not recommended due to potential behavior changes between test and non-test code. ```crystal module MyFileUtils def self.rm_rf(path) # ... end end inject_mock MyFileUtils ``` -------------------------------- ### Expect-Receive syntax for stubbing and verification Source: https://github.com/icy-arctic-fox/spectator/wiki/Stubs The expect-receive syntax combines stub definition with verification, ensuring a method was called. It's a more concise alternative to separate `allow` and `expect` statements. ```crystal class Driver def doit(thing) thing.call end end describe Driver do describe "#doit" do double :thing, call: 5 it "calls thing.call" do thing = double(:thing) allow(thing).to receive(:call).and_return(42) subject.doit(thing) expect(thing).to have_received(:call) end end end ``` -------------------------------- ### Injecting Mock with Stubbed Method Source: https://github.com/icy-arctic-fox/spectator/wiki/Mock-Modules Shows how to inject mock functionality into an existing module and provide a stub for a specific method using a block. ```crystal module MyFileUtils def self.rm_rf(path) # ... end end inject_mock MyFileUtils do stub def self.rm_rf(path) "Simulating deletion of #{path}" nil end end ``` -------------------------------- ### Providing Arguments to `subject` for Initialization Source: https://github.com/icy-arctic-fox/spectator/wiki/Subject Shows how to pass arguments to the implicit `subject` method to initialize the described class with specific parameters. This is useful for testing objects that require constructor arguments. ```crystal Spectator.describe MyShard::Models::User do describe "#initialize" do it "has the expected first name" do user = subject(first_name: "Bob") expect(user.first_name).to eq("Bob") end end end ``` -------------------------------- ### Nested Context Pre- and Post-condition Order Source: https://github.com/icy-arctic-fox/spectator/wiki/Hooks In nested contexts, outer pre-conditions execute before inner ones, and outer post-conditions execute after inner ones. This example illustrates the execution order (1, 2, 3, 4, 5) for hooks within nested describe blocks. ```crystal describe Something do let(original_array) { array.dup } pre_condition { is_expected.to_not be_nil } # 1 post_condition { expect(array).to match_array(original_array) } # 5 describe "#foo" do pre_condition { expect(subject.foo).to_not be_nil } # 2 post_condition { expect(array.size).to eq(3) } # 4 it "does a cool thing" do # 3 end end end ``` -------------------------------- ### Create and Use an Anonymous Double Source: https://github.com/icy-arctic-fox/spectator/wiki/Anonymous-Doubles Demonstrates the basic creation and usage of an anonymous double. All methods to be called must be specified during creation. ```crystal it "does something" do dbl = double(foo: 42) expect(dbl.foo).to eq(42) end ``` -------------------------------- ### Equivalent Assertion Syntaxes Source: https://github.com/icy-arctic-fox/spectator/wiki/Subject Shows various equivalent syntaxes for positive assertions using subjects, including `expect`, `is_expected`, `should`, and `is`. ```crystal expect(subject).to eq(42) is_expected.to eq(42) should eq(42) is(42) ``` -------------------------------- ### Check method calls with argument pattern matching Source: https://github.com/icy-arctic-fox/spectator/wiki/Stub-Expectations Use `with()` with basic pattern matching, such as types or regular expressions, to check arguments passed to a method. This allows for flexible argument verification. ```crystal dbl = double(:my_double) dbl.foo(42, "foobar") expect(dbl).to have_received(:foo).with(Int32, /foo/) ``` -------------------------------- ### Spectator Sample Context for Multiple Inputs Source: https://github.com/icy-arctic-fox/spectator/blob/master/README.md Illustrates the 'sample' context type, which repeats nested tests for a collection of values. Useful for testing features with various inputs like different integers. ```crystal # List of integers to test against. def various_integers [-7, -1, 0, 1, 42] end # Repeat nested tests for every value in `#various_integers`. sample various_integers do |int| # Example that checks if a fictitious method `#format` converts to strings. it "formats correctly" do expect(format(int)).to eq(int.to_s) end end ``` -------------------------------- ### Basic Stub Definition with expect Source: https://github.com/icy-arctic-fox/spectator/wiki/Stubs Similar to `allow`, the `expect` method can also be used to initiate the stub definition chain. ```crystal expect(dbl).to ... ```