icy-arctic-fox / spectator Goto Github PK
View Code? Open in Web Editor NEWFeature-rich testing framework for Crystal inspired by RSpec.
Home Page: https://gitlab.com/arctic-fox/spectator
License: MIT License
Feature-rich testing framework for Crystal inspired by RSpec.
Home Page: https://gitlab.com/arctic-fox/spectator
License: MIT License
Seems the removal of a workaround in v0.9.36 is causing problems. Removing require "openssl"
is causing some applications to get errors.
One instance is NeuraLegion/sslscan.cr#7
The error is:
Error: Channel is closed
Channel::ClosedError: Channel is closed
/usr/share/crystal/src/channel.cr:228:8 in 'send'
/usr/share/crystal/src/log/dispatch.cr:55:7 in 'dispatch'
/usr/share/crystal/src/log/backend.cr:24:5 in 'dispatch'
/usr/share/crystal/src/log/log.cr:36:3 in 'initialize'
src/sslscan/report.cr:68:5 in 'new'
src/sslscan/scanner.cr:25:7 in 'scan'
spec/sslscan/scanner_spec.cr:47:16 in '->'
/usr/share/crystal/src/primitives.cr:255:3 in 'value'
lib/spectator/src/spectator/matchers/type_matcher.cr:21:7 in 'match?'
lib/spectator/src/spectator/matchers/standard_matcher.cr:27:10 in 'match'
lib/spectator/src/spectator/expectations/expectation_partial.cr:23:20 in 'to'
spec/sslscan/scanner_spec.cr:47:7 in '__temp_68'
spec/sslscan/scanner_spec.cr:45:1 in '->'
/usr/share/crystal/src/primitives.cr:255:3 in 'call'
lib/spectator/src/spectator/test_wrapper.cr:27:7 in 'run'
lib/spectator/src/spectator/test_wrapper.cr:39:47 in '->'
/usr/share/crystal/src/primitives.cr:255:3 in 'run_example'
lib/spectator/src/spectator/runnable_example.cr:21:9 in 'capture_result'
lib/spectator/src/spectator/runnable_example.cr:10:7 in 'run_impl'
lib/spectator/src/spectator/example.cr:48:7 in 'run'
lib/spectator/src/spectator/harness.cr:31:7 in 'run'
lib/spectator/src/spectator/runner.cr:63:18 in 'run_example'
lib/spectator/src/spectator/runner.cr:38:9 in 'collect_results'
/usr/share/crystal/src/time.cr:356:5 in 'run'
lib/spectator/src/spectator.cr:105:5 in 'run'
lib/spectator/src/spectator.cr:79:29 in '->'
/usr/share/crystal/src/primitives.cr:255:3 in 'run'
/usr/share/crystal/src/crystal/main.cr:45:14 in 'main'
/usr/share/crystal/src/crystal/main.cr:119:3 in 'main'
__libc_start_main
_start
???
Changing the spec_helper.cr
file to include the src/
directory or openssl before Spectator fixes the issue. However, this shouldn't be an issue at all.
It would be useful to have RSpec's stub_const method, which would allow for stubbing ENV
.
I've noticed with 0.10 that the reported line/column of compilation errors are not always accurate. I am not sure whether this is a Spectator issue or really a Crystal compiler issue. When compiling a RSpec file which has been ran through ruby_crystal_codemod and half-ported over to Crystal/Spectator, crystal spec spec/foo_spec.cr --error-trace
will report a syntax error on a blank line just before another context
block and a mysterious internal error. When I remove --error-trace
, I can sometimes see the correct error message.
Error: expanding macro
In spec/reader_spec.cr:582:1
582 |
^-------
Error: expanding macro
In spec/reader_spec.cr:621:1
621 |
^------
Error: expanding macro
In spec/reader_spec.cr:666:1
666 |
^------
Error: expanding macro
Unhandled exception: Negative argument (ArgumentError)
from /crystal/src/string.cr:5024:13 in '*'
from /crystal/src/compiler/crystal/exception.cr:108:7 in 'append_error_indicator'
from /crystal/src/string/builder.cr:28:5 in 'error_body'
from /crystal/src/compiler/crystal/semantic/exception.cr:122:10 in 'append_to_s'
from /crystal/src/compiler/crystal/semantic/exception.cr:137:9 in 'append_to_s'
from /crystal/src/compiler/crystal/semantic/exception.cr:137:9 in 'append_to_s'
from /crystal/src/compiler/crystal/semantic/exception.cr:137:9 in 'append_to_s'
from /crystal/src/compiler/crystal/semantic/exception.cr:137:9 in 'append_to_s'
from /crystal/src/compiler/crystal/semantic/exception.cr:137:9 in 'append_to_s'
from /crystal/src/compiler/crystal/semantic/exception.cr:137:9 in 'append_to_s'
from /crystal/src/compiler/crystal/semantic/exception.cr:137:9 in 'append_to_s'
from /crystal/src/compiler/crystal/semantic/exception.cr:95:7 in 'run'
from /crystal/src/compiler/crystal.cr:11:1 in '__crystal_main'
from /crystal/src/crystal/main.cr:110:5 in 'main'
from src/env/__libc_start_main.c:94:2 in 'libc_start_main_stage2'
I ran into an odd constant scope resolution problem when I was porting some RSpec specs which defined test classes inside the describe
blocks:
require "./spec_helper"
Spectator.describe Test do
module TestFoo
class TestClass
end
end
let(foo) { TestFoo::TestClass }
describe "something else" do
module TestFoo
end
it "must resolve constants using the context the let() was defined in" do
p foo
end
end
end
foo
being defined in the outer-most describe block would resolve its constant in that context, not the describe block it was called from.
foo
is resolved in the describe
block it was called within. The inner-most TestFoo
shadows the outer-most TestFoo
, which causes the constant not to be found.
error in line 1
Error: while requiring "./spec/test_spec.cr"
In spec/test_spec.cr:3:11
3 | Spectator.describe Test do
^-------
Error: expanding macro
In spec/test_spec.cr:3:1
3 | Spectator.describe Test do
^
Error: expanding macro
There was a problem expanding macro 'describe'
Called macro defined in lib/spectator/src/spectator.cr:27:3
27 | macro describe(description, &block)
Which expanded to:
> 1 | # This macro creates the foundation for all specs.
> 2 | # Every group of examples is defined a separate module - `SpectatorExamples`.
> 3 | # There's multiple reasons for this.
> 4 | #
> 5 | # The first reason is to provide namespace isolation.
> 6 | # We don't want the spec code to accidentally pickup types and values from the `Spectator` module.
> 7 | # Another reason is that we need a root module to put all examples and groups in.
> 8 | # And lastly, the spec DSL needs to be given to the block of code somehow.
> 9 | # The DSL is included in the `SpectatorTest` class.
> 10 | #
> 11 | # For more information on how the DSL works, see the `DSL` module.
> 12 |
> 13 | # Root-level class that contains all examples and example groups.
> 14 | class SpectatorTest
> 15 | # Pass off the description argument and block to `DSL::StructureDSL.describe`.
> 16 | # That method will handle creating a new group for this spec.
> 17 | describe(Test) do
> 18 | module TestFoo
> 19 | class TestClass
> 20 | end
> 21 | end
> 22 | let(foo) do
> 23 | TestFoo::TestClass
> 24 | end
> 25 | describe("something else") do
> 26 | module TestFoo
> 27 | end
> 28 | it("must resolve constants using the context the let() was defined in") do
> 29 | p(foo)
> 30 | end
> 31 | end
> 32 | end
> 33 | end
> 34 |
Error: expanding macro
There was a problem expanding macro 'describe'
Called macro defined in lib/spectator/src/spectator.cr:27:3
27 | macro describe(description, &block)
Which expanded to:
> 1 | # This macro creates the foundation for all specs.
> 2 | # Every group of examples is defined a separate module - `SpectatorExamples`.
> 3 | # There's multiple reasons for this.
> 4 | #
> 5 | # The first reason is to provide namespace isolation.
> 6 | # We don't want the spec code to accidentally pickup types and values from the `Spectator` module.
> 7 | # Another reason is that we need a root module to put all examples and groups in.
> 8 | # And lastly, the spec DSL needs to be given to the block of code somehow.
> 9 | # The DSL is included in the `SpectatorTest` class.
> 10 | #
> 11 | # For more information on how the DSL works, see the `DSL` module.
> 12 |
> 13 | # Root-level class that contains all examples and example groups.
> 14 | class SpectatorTest
> 15 | # Pass off the description argument and block to `DSL::StructureDSL.describe`.
> 16 | # That method will handle creating a new group for this spec.
> 17 | describe(Test) do
> 18 | module TestFoo
> 19 | class TestClass
> 20 | end
> 21 | end
> 22 | let(foo) do
> 23 | TestFoo::TestClass
> 24 | end
> 25 | describe("something else") do
> 26 | module TestFoo
> 27 | end
> 28 | it("must resolve constants using the context the let() was defined in") do
> 29 | p(foo)
> 30 | end
> 31 | end
> 32 | end
> 33 | end
> 34 |
Error: expanding macro
There was a problem expanding macro 'describe'
Called macro defined in lib/spectator/src/spectator/dsl/groups.cr:48:5
48 | macro describe(what, &block)
Which expanded to:
> 1 | context(Test) do
> 2 | module TestFoo
> 3 | class TestClass
> 4 | end
> 5 | end
> 6 | let(foo) do
> 7 | TestFoo::TestClass
> 8 | end
> 9 | describe("something else") do
> 10 | module TestFoo
> 11 | end
> 12 | it("must resolve constants using the context the let() was defined in") do
> 13 | p(foo)
> 14 | end
> 15 | end
> 16 | end
> 17 |
Error: expanding macro
In spec/test_spec.cr:11:1
11 | describe "something else" do
^-------
Error: expanding macro
In spec/test_spec.cr:11:1
11 | describe "something else" do
^
Error: expanding macro
There was a problem expanding macro 'describe'
Called macro defined in lib/spectator/src/spectator/dsl/groups.cr:48:5
48 | macro describe(what, &block)
Which expanded to:
> 1 | context("something else") do
> 2 | module TestFoo
> 3 | end
> 4 | it("must resolve constants using the context the let() was defined in") do
> 5 | p(foo)
> 6 | end
> 7 | end
> 8 |
Error: expanding macro
In spec/test_spec.cr:15:5
15 | it "must resolve constants using the context the let() was defined in" do
^-
Error: expanding macro
In spec/test_spec.cr:15:5
15 | it "must resolve constants using the context the let() was defined in" do
^
Error: expanding macro
There was a problem expanding macro 'it'
Called macro defined in lib/spectator/src/spectator/dsl/examples.cr:6:5
6 | macro it(description = nil, &block)
Which expanded to:
> 1 |
> 2 | def __temp_33
> 3 | p(foo)
> 4 | end
> 5 |
> 6 |
> 7 |
> 8 | __temp_34 = ::Spectator::Source.new("/home/postmodern/test/crystal/spectator/spec/test_spec.cr", line: 15, end_line: 17)
> 9 |
> 10 | ::Spectator::SpecBuilder.add_example(
> 11 | "must resolve constants using the context the let() was defined in",
> 12 | __temp_34,
> 13 | SpectatorTest::Context__temp_27::Context__temp_31
> 14 | ) { |test| test.as(SpectatorTest::Context__temp_27::Context__temp_31).__temp_33 }
> 15 |
Error: instantiating 'SpectatorTest::Context__temp_27::Context__temp_31#__temp_33()'
In spec/test_spec.cr:16:9
16 | p foo
^--
Error: instantiating 'foo()'
In spec/test_spec.cr:9:3
9 | let(foo) { TestFoo::TestClass }
^
Error: expanding macro
There was a problem expanding macro 'let'
Called macro defined in lib/spectator/src/spectator/dsl/values.cr:3:5
3 | macro let(name, &block)
Which expanded to:
> 1 | @__temp_30 : ::Spectator::ValueWrapper?
> 2 |
> 3 | def foo
> 4 | TestFoo::TestClass
> 5 | end
> 6 |
> 7 | def foo
> 8 | if (wrapper = @__temp_30)
> 9 | wrapper.as(::Spectator::TypedValueWrapper(typeof(previous_def))).value
> 10 | else
> 11 | previous_def.tap do |value|
> 12 | @__temp_30 = ::Spectator::TypedValueWrapper.new(value)
> 13 | end
> 14 | end
> 15 | end
> 16 |
Error: instantiating 'previous_def()'
In spec/test_spec.cr:9:14
9 | let(foo) { TestFoo::TestClass }
^-----------------
Error: undefined constant TestFoo::TestClass
I am attempting to stub Process.run, but the variant which receives a block. I cannot find an example of how to successfully define the stub method which accepts the block and how to expect
/allow
the stub method to receive a block? My ultimate goal is to test when Process.run
raises a File::NotFoundError
exception and whether my code correctly rescues that exception and raises another more descriptive exception.
require "./spec_helper"
Spectator.describe Test do
mock Process do
stub self.run(command,shell,output,&block)
end
let(command) { "ls -l" }
before_each do
expect(Process).to receive(:run).with(command, shell: true, output: :pipe)
end
it "must stub Process.run" do
Process.run(command, shell: true, output: :pipe) do |process|
end
end
end
Showing last frame. Use --error-trace for full trace.
There was a problem expanding macro 'stub'
Code in spec/test_spec.cr:6:11
6 | end
^
Called macro defined in lib/spectator/src/spectator/mocks/stubs.cr:3:13
3 | private macro stub(definition, *types, _file = __FILE__, _line = __LINE__, return_type = :undefined, &block)
Which expanded to:
> 26 | __temp_72.mocks.record_call(self, __temp_74)
> 27 | if (__temp_75 = __temp_72.mocks.find_stub(self, __temp_74))
> 28 | return __temp_75.call!(__temp_73) { previous_def { |*__temp_76| yield *__temp_76 } }
^-----------
Error: there is no previous definition of 'run'
I'm trying to test a class that makes a HTTP GET request using Spectator. The previous test, using stdlib's spec
, works just fine and running the application is also successful, but when using Spectator it fails.
Here's a reduced case that fails:
require "spectator"
require "http"
Spectator.describe "something" do
it "makes a request" do
url = "http http://httpbin.org/deflate "
body = HTTP::Client.get(url).body
expect(body).not_to be_nil
end
end
Upon running crystal spec
I get the following error:
Module validation failed: !dbg attachment points at wrong subprogram for function
!10 = distinct !DISubprogram(name: "initialize", linkageName: "initialize", scope: !5, file: !5, line: 115, type: !6, scopeLine: 115, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !0, retainedNodes: !2)
void (%"OpenSSL::BIO"*, %TCPSocket*)* @"*OpenSSL::BIO#initialize<TCPSocket>:Nil"
%2 = call i8** @"~OpenSSL::BIO::CRYSTAL_BIO:read"(), !dbg !12
!12 = !DILocation(line: 14, column: 5, scope: !13)
!13 = distinct !DISubprogram(name: "set_data", linkageName: "set_data", scope: !5, file: !5, line: 13, type: !6, scopeLine: 13, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !0, retainedNodes: !2)
!13 = distinct !DISubprogram(name: "set_data", linkageName: "set_data", scope: !5, file: !5, line: 13, type: !6, scopeLine: 13, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !0, retainedNodes: !2)
(Exception)
from ???
from ???
from ???
from ???
from ???
from ???
from ???
from __crystal_main
from main
from __libc_start_main
from _start
from ???
Error: you've found a bug in the Crystal compiler. Please open an issue, including source code that will allow us to reproduce the bug: https://github.com/crystal-lang/crystal/issues
Any idea what might be causing it?
Ran into this when porting RSpec tests which stubs the new
method so that the tests control the instance object that's returned.
require "./spec_helper"
Spectator.describe Test do
module TestFoo
class TestClass
def initialize
end
# the method we are testing
def self.test
new().test
end
# the method we want to ensure gets called
def test
end
end
end
let(test_class) { TestFoo::TestClass }
let(test_instance) { test_class.new }
describe "something else" do
mock TestFoo::TestClass do
stub new
end
it "must test when new is called" do
expect(test_class).to receive(:new).with(no_args).and_return(test_instance)
expect(test_instance).to receive(:test)
test_class.test
end
end
end
Test
something else
must test when new is called
Failures:
1) Test something else must test when new is called
Failure: test_class did not receive #new(#<Spectator::Mocks::NoArguments:0x7f3fba62cbe0>) : SpectatorTest::Context__temp_27::TestFoo::TestClass at spec/test_spec.cr:27 at least once with any arguments
expected: At least once with any arguments
received: 0 time(s)
# spec/test_spec.cr:26
Finished in 466 microseconds
1 examples, 1 failures, 0 errors, 0 pending
Related to #19 but expanded to support passing a line number within the grouping of a spec. If the line number matches a context block, every spec within that context should be run. The same goes for the describe block. If the line number falls on a specific spec then that should be run specifically, still.
Suddenly started getting this error with spectator ~> 0.9 and ~> 0.10.
$ crystal spec --error-trace
error in line 4
Error: while requiring "./spec/chars_spec.cr"
In spec/chars_spec.cr:4:11
4 | Spectator.describe Chars do
^-------
Error: expanding macro
In spec/chars_spec.cr:4:1
4 | Spectator.describe Chars do
^
Error: expanding macro
There was a problem expanding macro 'describe'
Called macro defined in macro 'macro_140000956373072'
46 | macro describe(description, *tags, **metadata, &block)
Which expanded to:
> 1 | class ::SpectatorTestContext
> 2 | describe(Chars, ) do
> 3 | describe("NUMERIC") do
> 4 | subject do
> 5 | Chars::NUMERIC
> 6 | end
> 7 | it("should provide a numeric CharSet") do
> 8 | (expect(subject)).to(be =~ "0123456789")
> 9 | end
> 10 | end
> 11 | describe("OCTAL") do
> 12 | subject do
> 13 | Chars::OCTAL
> 14 | end
> 15 | it("should provide an octal CharSet") do
> 16 | (expect(subject)).to(be =~ "01234567")
> 17 | end
> 18 | end
> 19 | describe("UPPERCASE_HEXADECIMAL") do
> 20 | subject do
> 21 | Chars::UPPERCASE_HEXADECIMAL
> 22 | end
> 23 | it("should provide an upper-case hexadecimal CharSet") do
> 24 | (expect(subject)).to(be =~ "0123456789ABCDEF")
> 25 | end
> 26 | end
> 27 | describe("LOWERCASE_HEXADECIMAL") do
> 28 | subject do
> 29 | Chars::LOWERCASE_HEXADECIMAL
> 30 | end
> 31 | it("should provide a lower-case hexadecimal CharSet") do
> 32 | (expect(subject)).to(be =~ "0123456789abcdef")
> 33 | end
> 34 | end
> 35 | describe("HEXADECIMAL") do
> 36 | subject do
> 37 | Chars::HEXADECIMAL
> 38 | end
> 39 | it("should provide a hexadecimal CharSet") do
> 40 | (expect(subject)).to(be =~ "0123456789ABCDEFabcdef")
> 41 | end
> 42 | end
> 43 | describe("UPPERCASE_ALPHA") do
> 44 | subject do
> 45 | Chars::UPPERCASE_ALPHA
> 46 | end
> 47 | it("should provide an upper-case alpha CharSet") do
> 48 | (expect(subject)).to(be =~ "ABCDEFGHIJKLMNOPQRSTUVWXYZ")
> 49 | end
> 50 | end
> 51 | describe("LOWERCASE_ALPHA") do
> 52 | subject do
> 53 | Chars::LOWERCASE_ALPHA
> 54 | end
> 55 | it("should provide a lower-case alpha CharSet") do
> 56 | (expect(subject)).to(be =~ "abcdefghijklmnopqrstuvwxyz")
> 57 | end
> 58 | end
> 59 | describe("ALPHA") do
> 60 | subject do
> 61 | Chars::ALPHA
> 62 | end
> 63 | it("should provide an alpha CharSet") do
> 64 | (expect(subject)).to(be =~ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")
> 65 | end
> 66 | end
> 67 | describe("ALPHA_NUMERIC") do
> 68 | subject do
> 69 | Chars::ALPHA_NUMERIC
> 70 | end
> 71 | it("should provide an alpha-numeric CharSet") do
> 72 | (expect(subject)).to(be =~ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")
> 73 | end
> 74 | end
> 75 | describe("VISIBLE") do
> 76 | subject do
> 77 | Chars::VISIBLE
> 78 | end
> 79 | it("should provide a visible CharSet") do
> 80 | (expect(subject)).to(be =~ "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~")
> 81 | end
> 82 | end
> 83 | describe("SPACE") do
> 84 | subject do
> 85 | Chars::SPACE
> 86 | end
> 87 | it("should provide a space CharSet") do
> 88 | (expect(subject)).to(be =~ "\t\n\v\f\r ")
> 89 | end
> 90 | end
> 91 | describe("PUNCTUATION") do
> 92 | subject do
> 93 | Chars::PUNCTUATION
> 94 | end
> 95 | it("should provide a punctuation CharSet") do
> 96 | (expect(subject)).to(be =~ " !\"'(),-.:;?[]`{}~")
> 97 | end
> 98 | end
> 99 | describe("SYMBOLS") do
> 100 | subject do
> 101 | Chars::SYMBOLS
> 102 | end
> 103 | it("should provide a symbols CharSet") do
> 104 | (expect(subject)).to(be =~ " !\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~")
> 105 | end
> 106 | end
> 107 | end
> 108 | end
> 109 |
Error: expanding macro
In spec/chars_spec.cr:15:11
15 |
^-------
Error: expanding macro
In spec/chars_spec.cr:8:1
8 | it "should provide a numeric CharSet" do
^-
Error: expanding macro
In spec/chars_spec.cr:8:1
8 | it "should provide a numeric CharSet" do
^
Error: expanding macro
There was a problem expanding macro 'it'
Called macro defined in macro 'define_example'
17 | macro it(what = nil, *tags, **metadata, &block)
Which expanded to:
> 1 |
> 2 |
> 3 |
> 4 | _spectator_metadata(__temp_36, :metadata, )
> 5 | _spectator_metadata(__temp_860, __temp_36, )
> 6 |
> 7 |
> 8 |
> 9 |
> 10 | private def __temp_861() : Nil
> 11 | (expect(subject)).to(be =~ "0123456789")
> 12 | end
> 13 |
> 14 | ::Spectator::DSL::Builder.add_example(
> 15 | _spectator_example_name("should provide a numeric CharSet"),
> 16 | ::Spectator::Location.new("/data/home/postmodern/code/crystal/chars.cr/spec/chars_spec.cr", 8, 10),
> 17 | -> { new.as(::Spectator::Context) },
> 18 | __temp_860
> 19 | ) do |example|
> 20 | example.with_context(SpectatorTestContext::Group__temp_852::Group__temp_856) do
> 21 |
> 22 | __temp_861
> 23 |
> 24 | end
> 25 | end
> 26 |
> 27 |
> 28 |
Error: instantiating 'Spectator::Example#with_context(SpectatorTestContext::Group__temp_852::Group__temp_856.class)'
In spec/chars_spec.cr:8:1
8 | it "should provide a numeric CharSet" do
^
Error: expanding macro
There was a problem expanding macro 'it'
Called macro defined in macro 'define_example'
17 | macro it(what = nil, *tags, **metadata, &block)
Which expanded to:
> 1 |
> 2 |
> 3 |
> 4 | _spectator_metadata(__temp_36, :metadata, )
> 5 | _spectator_metadata(__temp_860, __temp_36, )
> 6 |
> 7 |
> 8 |
> 9 |
> 10 | private def __temp_861() : Nil
> 11 | (expect(subject)).to(be =~ "0123456789")
> 12 | end
> 13 |
> 14 | ::Spectator::DSL::Builder.add_example(
> 15 | _spectator_example_name("should provide a numeric CharSet"),
> 16 | ::Spectator::Location.new("/data/home/postmodern/code/crystal/chars.cr/spec/chars_spec.cr", 8, 10),
> 17 | -> { new.as(::Spectator::Context) },
> 18 | __temp_860
> 19 | ) do |example|
> 20 | example.with_context(SpectatorTestContext::Group__temp_852::Group__temp_856) do
> 21 |
> 22 | __temp_861
> 23 |
> 24 | end
> 25 | end
> 26 |
> 27 |
> 28 |
Error: instantiating 'Spectator::Example#with_context(SpectatorTestContext::Group__temp_852::Group__temp_856.class)'
In spec/chars_spec.cr:8:1
8 | it "should provide a numeric CharSet" do
^
Error: expanding macro
There was a problem expanding macro 'it'
Called macro defined in macro 'define_example'
17 | macro it(what = nil, *tags, **metadata, &block)
Which expanded to:
> 1 |
> 2 |
> 3 |
> 4 | _spectator_metadata(__temp_36, :metadata, )
> 5 | _spectator_metadata(__temp_860, __temp_36, )
> 6 |
> 7 |
> 8 |
> 9 |
> 10 | private def __temp_861() : Nil
> 11 | (expect(subject)).to(be =~ "0123456789")
> 12 | end
> 13 |
> 14 | ::Spectator::DSL::Builder.add_example(
> 15 | _spectator_example_name("should provide a numeric CharSet"),
> 16 | ::Spectator::Location.new("/data/home/postmodern/code/crystal/chars.cr/spec/chars_spec.cr", 8, 10),
> 17 | -> { new.as(::Spectator::Context) },
> 18 | __temp_860
> 19 | ) do |example|
> 20 | example.with_context(SpectatorTestContext::Group__temp_852::Group__temp_856) do
> 21 |
> 22 | __temp_861
> 23 |
> 24 | end
> 25 | end
> 26 |
> 27 |
> 28 |
Error: instantiating '__temp_861()'
In spec/chars_spec.cr:9:29
9 | expect(subject).to be =~ "0123456789"
^-
Error: instantiating 'Spectator::Matchers::TruthyMatcher#=~(String)'
In lib/spectator/src/spectator/matchers/truthy_matcher.cr:101:18
101 | expected = TestValue.new(value)
^--------
Error: undefined constant TestValue
Porting some RSpec specs over to Crystal and noticed that is no equivalent for no_args
.
expect(subject).to receive(:new).with(no_args).and_return(instance)
Not sure if thee's a workaround?
Thanks for this great shard, works splendidly for me! ๐
Super minor thing I noticed:
It doesn't seem to print the random seed when -r
is given.
Would be nice if it could do that, so that a failure that
depends on test-order can be easily reproduced.
When using an abstract class in the following example, i get an error that the method register_hook
is not being implemented.
require "./spec_helper"
abstract class SdkInterface
abstract def register_hook(name, &block)
end
class Example
def initialize(@sdk : Sdk)
end
def configure
@sdk.register_hook("name") do
nil
end
end
end
class Sdk < SdkInterface
def initialize
end
def register_hook(name, &block)
nil
end
end
Spectator.describe Example do
mock Sdk do
stub register_hook(name, &block)
end
describe "#configure" do
it "registers a block on configure" do
sdk = Sdk.new
example_class = Example.new(sdk)
allow(sdk).to receive(register_hook())
example_class.configure
expect(sdk).to have_received(register_hook()).with("name")
end
end
end
If i change the Sdk
class to not inherit from SdkInterface
it works as expected.
I want to create a class mock that has the following method:
def [](key : String)
find_key key
end
I couldn't find a way to stub that. When I try something like this:
mock MyMock do
stub [](key)
end
It produces an error like this:
7 | stub [](name)
^
Error: for empty arrays use '[] of ElementType'
Is there any way to achieve this?
I was trying to test chars.cr against Crystal 1.6.2 and Spectator 0.11.4, but got this strange error coming from deep within enumerable.cr
. The beginning of the error is a macro expansion of a method stub. I assume I need to update how I'm defining the stubs?
mock Chars::CharSet do
stub :|, other : Chars::CharSet
end
let(other) { described_class.new }
let(return_value) { described_class.new }
it "must call #+" do
expect(subject).to receive(:|).with(other).and_return(return_value)
expect(subject + other).to be(return_value)
end
$ crystal spec --error-trace
There was a problem expanding macro 'finished'
Called macro defined in macro 'define_subtype'
46 | macro finished
Which expanded to:
> 1 | stub_type Chars::CharSet
> 2 |
> 3 | stub(:|, other : Chars::CharSet)
> 4 |
Error: expanding macro
There was a problem expanding macro 'stub_type'
Called macro defined in lib/spectator/src/spectator/mocks/stubbable.cr:339:13
339 | private macro stub_type(type_name = @type)
Which expanded to:
> 1 |
> 2 |
> 3 |
> 4 | default_stub def colorize(
> 5 | r : UInt8, g : UInt8, b : UInt8,
> 6 |
> 7 |
> 8 | )
> 9 | super
> 10 | end
> 11 |
> 12 | default_stub def colorize(
> 13 | fore : UInt8,
> 14 |
> 15 |
> 16 | )
> 17 | super
> 18 | end
> 19 |
> 20 | default_stub def colorize(
> 21 | fore : Symbol,
> 22 |
> 23 |
> 24 | )
> 25 | super
> 26 | end
> 27 |
> 28 | default_stub def colorize(
> 29 | fore : Color,
> 30 |
> 31 |
> 32 | )
> 33 | super
> 34 | end
> 35 |
> 36 | default_stub def colorize(
> 37 |
> 38 |
> 39 |
> 40 | ) : Colorize::Object
> 41 | super
> 42 | end
> 43 |
> 44 |
> 45 |
> 46 |
> 47 |
> 48 | abstract_stub def ==(
> 49 | other,
> 50 |
> 51 |
> 52 | )
> 53 | super
> 54 | end
> 55 |
> 56 | default_stub def !=(
> 57 | other,
> 58 |
> 59 |
> 60 | )
> 61 | super
> 62 | end
> 63 |
> 64 | default_stub def !~(
> 65 | other,
> 66 |
> 67 |
> 68 | )
> 69 | super
> 70 | end
> 71 |
> 72 | default_stub def ===(
> 73 | other : JSON::Any,
> 74 |
> 75 |
> 76 | )
> 77 | super
> 78 | end
> 79 |
> 80 | default_stub def ===(
> 81 | other,
> 82 |
> 83 |
> 84 | )
> 85 | super
> 86 | end
> 87 |
> 88 | default_stub def =~(
> 89 | other,
> 90 |
> 91 |
> 92 | )
> 93 | super
> 94 | end
> 95 |
> 96 | abstract_stub def hash(
> 97 | hasher,
> 98 |
> 99 |
> 100 | )
> 101 | super
> 102 | end
> 103 |
> 104 | default_stub def hash(
> 105 |
> 106 |
> 107 |
> 108 | )
> 109 | super
> 110 | end
> 111 |
> 112 | abstract_stub def to_s(
> 113 | io : IO,
> 114 |
> 115 |
> 116 | ) : Nil
> 117 | super
> 118 | end
> 119 |
> 120 | default_stub def to_s(
> 121 |
> 122 |
> 123 |
> 124 | ) : String
> 125 | super
> 126 | end
> 127 |
> 128 | default_stub def inspect(
> 129 | io : IO,
> 130 |
> 131 |
> 132 | ) : Nil
> 133 | super
> 134 | end
> 135 |
> 136 | default_stub def inspect(
> 137 |
> 138 |
> 139 |
> 140 | ) : String
> 141 | super
> 142 | end
> 143 |
> 144 | default_stub def pretty_print(
> 145 | pp : PrettyPrint,
> 146 |
> 147 |
> 148 | ) : Nil
> 149 | super
> 150 | end
> 151 |
> 152 | default_stub def pretty_inspect(
> 153 | width = 79, newline = "\n", indent = 0,
> 154 |
> 155 |
> 156 | ) : String
> 157 | super
> 158 | end
> 159 |
> 160 | default_stub def tap(
> 161 |
> 162 |
> 163 | &
> 164 | )
> 165 | super { |*__temp_1063| yield *__temp_1063 }
> 166 | end
> 167 |
> 168 | default_stub def try(
> 169 |
> 170 |
> 171 | &
> 172 | )
> 173 | super { |*__temp_1063| yield *__temp_1063 }
> 174 | end
> 175 |
> 176 | default_stub def in?(
> 177 | collection : Object,
> 178 |
> 179 |
> 180 | ) : Bool
> 181 | super
> 182 | end
> 183 |
> 184 | default_stub def in?(
> 185 | *values : Object,
> 186 |
> 187 |
> 188 | ) : Bool
> 189 | super
> 190 | end
> 191 |
> 192 | default_stub def not_nil!(
> 193 |
> 194 |
> 195 |
> 196 | )
> 197 | super
> 198 | end
> 199 |
> 200 | default_stub def itself(
> 201 |
> 202 |
> 203 |
> 204 | )
> 205 | super
> 206 | end
> 207 |
> 208 | abstract_stub def dup(
> 209 |
> 210 |
> 211 |
> 212 | )
> 213 | super
> 214 | end
> 215 |
> 216 | default_stub def unsafe_as(
> 217 | type : T.class,
> 218 |
> 219 |
> 220 | ) forall T
> 221 | super
> 222 | end
> 223 |
> 224 | default_stub def crystal_type_id(
> 225 |
> 226 |
> 227 |
> 228 | ) : Int32
> 229 | super
> 230 | end
> 231 |
> 232 | default_stub def to_json(
> 233 | io : IO,
> 234 |
> 235 |
> 236 | ) : Nil
> 237 | super
> 238 | end
> 239 |
> 240 | default_stub def to_json(
> 241 |
> 242 |
> 243 |
> 244 | ) : String
> 245 | super
> 246 | end
> 247 |
> 248 | default_stub def to_pretty_json(
> 249 | indent : String = " ",
> 250 |
> 251 |
> 252 | ) : String
> 253 | super
> 254 | end
> 255 |
> 256 | default_stub def to_pretty_json(
> 257 | io : IO, indent : String = " ",
> 258 |
> 259 |
> 260 | ) : Nil
> 261 | super
> 262 | end
> 263 |
> 264 |
> 265 |
> 266 | default_stub protected def self.set_crystal_type_id(
> 267 | ptr,
> 268 |
> 269 |
> 270 | )
> 271 | super
> 272 | end
> 273 |
> 274 | default_stub def self.from_json(
> 275 | string_or_io, root : String,
> 276 |
> 277 |
> 278 | )
> 279 | super
> 280 | end
> 281 |
> 282 | default_stub def self.from_json(
> 283 | string_or_io,
> 284 |
> 285 |
> 286 | )
> 287 | super
> 288 | end
> 289 |
> 290 |
> 291 |
> 292 | default_stub def object_id(
> 293 |
> 294 |
> 295 |
> 296 | ) : UInt64
> 297 | super
> 298 | end
> 299 |
> 300 | default_stub def ==(
> 301 | other : self,
> 302 |
> 303 |
> 304 | )
> 305 | super
> 306 | end
> 307 |
> 308 | default_stub def ==(
> 309 | other : JSON::Any,
> 310 |
> 311 |
> 312 | )
> 313 | super
> 314 | end
> 315 |
> 316 | default_stub def ==(
> 317 | other,
> 318 |
> 319 |
> 320 | )
> 321 | super
> 322 | end
> 323 |
> 324 | default_stub def same?(
> 325 | other : Reference,
> 326 |
> 327 |
> 328 | ) : Bool
> 329 | super
> 330 | end
> 331 |
> 332 | default_stub def same?(
> 333 | other : Nil,
> 334 |
> 335 |
> 336 | )
> 337 | super
> 338 | end
> 339 |
> 340 | default_stub def dup(
> 341 |
> 342 |
> 343 |
> 344 | )
> 345 | super
> 346 | end
> 347 |
> 348 | default_stub def hash(
> 349 | hasher,
> 350 |
> 351 |
> 352 | )
> 353 | super
> 354 | end
> 355 |
> 356 | default_stub def inspect(
> 357 | io : IO,
> 358 |
> 359 |
> 360 | ) : Nil
> 361 | super
> 362 | end
> 363 |
> 364 | default_stub def pretty_print(
> 365 | pp,
> 366 |
> 367 |
> 368 | ) : Nil
> 369 | super
> 370 | end
> 371 |
> 372 | default_stub def to_s(
> 373 | io : IO,
> 374 |
> 375 |
> 376 | ) : Nil
> 377 | super
> 378 | end
> 379 |
> 380 | default_stub private def exec_recursive(
> 381 | method,
> 382 |
> 383 | &
> 384 | )
> 385 | super { |*__temp_1063| yield *__temp_1063 }
> 386 | end
> 387 |
> 388 | default_stub private def exec_recursive_clone(
> 389 |
> 390 |
> 391 | &
> 392 | )
> 393 | super { |*__temp_1063| yield *__temp_1063 }
> 394 | end
> 395 |
> 396 |
> 397 |
> 398 |
> 399 |
> 400 | abstract_stub def each(
> 401 |
> 402 |
> 403 | & : (T ->)
> 404 | )
> 405 | super { |*__temp_1063| yield *__temp_1063 }
> 406 | end
> 407 |
> 408 | default_stub def all?(
> 409 |
> 410 |
> 411 | & : (T ->)
> 412 | ) : Bool
> 413 | super { |*__temp_1063| yield *__temp_1063 }
> 414 | end
> 415 |
> 416 | default_stub def all?(
> 417 | pattern,
> 418 |
> 419 |
> 420 | ) : Bool
> 421 | super
> 422 | end
> 423 |
> 424 | default_stub def all?(
> 425 |
> 426 |
> 427 |
> 428 | ) : Bool
> 429 | super
> 430 | end
> 431 |
> 432 | default_stub def any?(
> 433 |
> 434 |
> 435 | & : (T ->)
> 436 | ) : Bool
> 437 | super { |*__temp_1063| yield *__temp_1063 }
> 438 | end
> 439 |
> 440 | default_stub def any?(
> 441 | pattern,
> 442 |
> 443 |
> 444 | ) : Bool
> 445 | super
> 446 | end
> 447 |
> 448 | default_stub def any?(
> 449 |
> 450 |
> 451 |
> 452 | ) : Bool
> 453 | super
> 454 | end
> 455 |
> 456 | default_stub def chunks(
> 457 |
> 458 |
> 459 | &block : (T -> U)
> 460 | ) forall U
> 461 | super { |*__temp_1063| yield *__temp_1063 }
> 462 | end
> 463 |
> 464 | default_stub private def chunks_internal(
> 465 | original_block : (T -> U),
> 466 |
> 467 | &
> 468 | ) forall U
> 469 | super { |*__temp_1063| yield *__temp_1063 }
> 470 | end
> 471 |
> 472 | default_stub def compact_map(
> 473 |
> 474 |
> 475 | & : (T -> _)
> 476 | )
> 477 | super { |*__temp_1063| yield *__temp_1063 }
> 478 | end
> 479 |
> 480 | default_stub def count(
> 481 |
> 482 |
> 483 | & : (T ->)
> 484 | ) : Int32
> 485 | super { |*__temp_1063| yield *__temp_1063 }
> 486 | end
> 487 |
> 488 | default_stub def count(
> 489 | item,
> 490 |
> 491 |
> 492 | ) : Int32
> 493 | super
> 494 | end
> 495 |
> 496 | default_stub def cycle(
> 497 | n,
> 498 |
> 499 | & : (T ->)
> 500 | ) : Nil
> 501 | super { |*__temp_1063| yield *__temp_1063 }
> 502 | end
> 503 |
> 504 | default_stub def cycle(
> 505 |
> 506 |
> 507 | & : (T ->)
> 508 | ) : Nil
> 509 | super { |*__temp_1063| yield *__temp_1063 }
> 510 | end
> 511 |
> 512 | default_stub def each_cons(
> 513 | count : Int, reuse = false,
> 514 |
> 515 | &
> 516 | )
> 517 | super { |*__temp_1063| yield *__temp_1063 }
> 518 | end
> 519 |
> 520 | default_stub private def each_cons_internal(
> 521 | count : Int, reuse, cons,
> 522 |
> 523 | &
> 524 | )
> 525 | super { |*__temp_1063| yield *__temp_1063 }
> 526 | end
> 527 |
> 528 | default_stub def each_cons_pair(
> 529 |
> 530 |
> 531 | & : (T, T ->)
> 532 | ) : Nil
> 533 | super { |*__temp_1063| yield *__temp_1063 }
> 534 | end
> 535 |
> 536 | default_stub def each_slice(
> 537 | count : Int, reuse = false,
> 538 |
> 539 | &
> 540 | )
> 541 | super { |*__temp_1063| yield *__temp_1063 }
> 542 | end
> 543 |
> 544 | default_stub private def each_slice_internal(
> 545 | count : Int, type, reuse,
> 546 |
> 547 | &
> 548 | )
> 549 | super { |*__temp_1063| yield *__temp_1063 }
> 550 | end
> 551 |
> 552 | default_stub def each_with_index(
> 553 | offset = 0,
> 554 |
> 555 | &
> 556 | )
> 557 | super { |*__temp_1063| yield *__temp_1063 }
> 558 | end
> 559 |
> 560 | default_stub def each_with_object(
> 561 | obj : U,
> 562 |
> 563 | & : (T, U ->)
> 564 | ) : U forall U
> 565 | super { |*__temp_1063| yield *__temp_1063 }
> 566 | end
> 567 |
> 568 | default_stub def find(
> 569 | if_none = nil,
> 570 |
> 571 | & : (T ->)
> 572 | )
> 573 | super { |*__temp_1063| yield *__temp_1063 }
> 574 | end
> 575 |
> 576 | default_stub def find!(
> 577 |
> 578 |
> 579 | & : (T ->)
> 580 | ) : T
> 581 | super { |*__temp_1063| yield *__temp_1063 }
> 582 | end
> 583 |
> 584 | default_stub def first(
> 585 |
> 586 |
> 587 | &
> 588 | )
> 589 | super { |*__temp_1063| yield *__temp_1063 }
> 590 | end
> 591 |
> 592 | default_stub def first(
> 593 | count : Int,
> 594 |
> 595 |
> 596 | ) : Array(T)
> 597 | super
> 598 | end
> 599 |
> 600 | default_stub def first(
> 601 |
> 602 |
> 603 |
> 604 | ) : T
> 605 | super
> 606 | end
> 607 |
> 608 | default_stub def first?(
> 609 |
> 610 |
> 611 |
> 612 | ) : T | ::Nil
> 613 | super
> 614 | end
> 615 |
> 616 | default_stub def flat_map(
> 617 |
> 618 |
> 619 | & : (T -> _)
> 620 | )
> 621 | super { |*__temp_1063| yield *__temp_1063 }
> 622 | end
> 623 |
> 624 | default_stub private def flat_map_type(
> 625 | elem,
> 626 |
> 627 |
> 628 | )
> 629 | super
> 630 | end
> 631 |
> 632 | default_stub def group_by(
> 633 |
> 634 |
> 635 | & : (T -> U)
> 636 | ) forall U
> 637 | super { |*__temp_1063| yield *__temp_1063 }
> 638 | end
> 639 |
> 640 | default_stub def in_groups_of(
> 641 | size : Int, filled_up_with : U = nil,
> 642 |
> 643 |
> 644 | ) forall U
> 645 | super
> 646 | end
> 647 |
> 648 | default_stub def in_groups_of(
> 649 | size : Int, filled_up_with : U = nil, reuse = false,
> 650 |
> 651 | &
> 652 | ) forall U
> 653 | super { |*__temp_1063| yield *__temp_1063 }
> 654 | end
> 655 |
> 656 | default_stub def includes?(
> 657 | obj,
> 658 |
> 659 |
> 660 | ) : Bool
> 661 | super
> 662 | end
> 663 |
> 664 | default_stub def index(
> 665 |
> 666 |
> 667 | & : (T ->)
> 668 | ) : Int32 | ::Nil
> 669 | super { |*__temp_1063| yield *__temp_1063 }
> 670 | end
> 671 |
> 672 | default_stub def index(
> 673 | obj,
> 674 |
> 675 |
> 676 | ) : Int32 | ::Nil
> 677 | super
> 678 | end
> 679 |
> 680 | default_stub def index!(
> 681 |
> 682 |
> 683 | & : (T ->)
> 684 | ) : Int32
> 685 | super { |*__temp_1063| yield *__temp_1063 }
> 686 | end
> 687 |
> 688 | default_stub def index!(
> 689 | obj,
> 690 |
> 691 |
> 692 | ) : Int32
> 693 | super
> 694 | end
> 695 |
> 696 | default_stub def index_by(
> 697 |
> 698 |
> 699 | & : (T -> U)
> 700 | ) : Hash(U, T) forall U
> 701 | super { |*__temp_1063| yield *__temp_1063 }
> 702 | end
> 703 |
> 704 | default_stub def reduce(
> 705 | memo,
> 706 |
> 707 | &
> 708 | )
> 709 | super { |*__temp_1063| yield *__temp_1063 }
> 710 | end
> 711 |
> 712 | default_stub def reduce(
> 713 |
> 714 |
> 715 | &
> 716 | )
> 717 | super { |*__temp_1063| yield *__temp_1063 }
> 718 | end
> 719 |
> 720 | default_stub def reduce?(
> 721 |
> 722 |
> 723 | &
> 724 | )
> 725 | super { |*__temp_1063| yield *__temp_1063 }
> 726 | end
> 727 |
> 728 | default_stub def accumulate(
> 729 | initial : U,
> 730 |
> 731 |
> 732 | ) : Array(U) forall U
> 733 | super
> 734 | end
> 735 |
> 736 | default_stub def accumulate(
> 737 |
> 738 |
> 739 |
> 740 | ) : Array(T)
> 741 | super
> 742 | end
> 743 |
> 744 | default_stub def accumulate(
> 745 | initial : U,
> 746 |
> 747 | &block : (U, T -> U)
> 748 | ) : Array(U) forall U
> 749 | super { |*__temp_1063| yield *__temp_1063 }
> 750 | end
> 751 |
> 752 | default_stub def accumulate(
> 753 |
> 754 |
> 755 | &block : (T, T -> T)
> 756 | ) : Array(T)
> 757 | super { |*__temp_1063| yield *__temp_1063 }
> 758 | end
> 759 |
> 760 | default_stub def join(
> 761 | io : IO, separator = "",
> 762 |
> 763 |
> 764 | ) : Nil
> 765 | super
> 766 | end
> 767 |
> 768 | default_stub def join(
> 769 | separator, io : IO,
> 770 |
> 771 |
> 772 | ) : Nil
> 773 | super
> 774 | end
> 775 |
> 776 | default_stub def join(
> 777 | separator = "",
> 778 |
> 779 |
> 780 | ) : String
> 781 | super
> 782 | end
> 783 |
> 784 | default_stub def join(
> 785 | io : IO, separator = "",
> 786 |
> 787 | & : (T, IO ->)
> 788 | )
> 789 | super { |*__temp_1063| yield *__temp_1063 }
> 790 | end
> 791 |
> 792 | default_stub def join(
> 793 | separator, io : IO,
> 794 |
> 795 | &
> 796 | )
> 797 | super { |*__temp_1063| yield *__temp_1063 }
> 798 | end
> 799 |
> 800 | default_stub def join(
> 801 | separator = "",
> 802 |
> 803 | & : (T ->)
> 804 | )
> 805 | super { |*__temp_1063| yield *__temp_1063 }
> 806 | end
> 807 |
> 808 | default_stub def map(
> 809 |
> 810 |
> 811 | & : (T -> U)
> 812 | ) : Array(U) forall U
> 813 | super { |*__temp_1063| yield *__temp_1063 }
> 814 | end
> 815 |
> 816 | default_stub def map_with_index(
> 817 | offset = 0,
> 818 |
> 819 | & : (T, Int32 -> U)
> 820 | ) : Array(U) forall U
> 821 | super { |*__temp_1063| yield *__temp_1063 }
> 822 | end
> 823 |
> 824 | default_stub def max(
> 825 |
> 826 |
> 827 |
> 828 | ) : T
> 829 | super
> 830 | end
> 831 |
> 832 | default_stub def max?(
> 833 |
> 834 |
> 835 |
> 836 | ) : T | ::Nil
> 837 | super
> 838 | end
> 839 |
> 840 | default_stub def max_by(
> 841 |
> 842 |
> 843 | & : (T -> U)
> 844 | ) : T forall U
> 845 | super { |*__temp_1063| yield *__temp_1063 }
> 846 | end
> 847 |
> 848 | default_stub def max_by?(
> 849 |
> 850 |
> 851 | & : (T -> U)
> 852 | ) : T | ::Nil forall U
> 853 | super { |*__temp_1063| yield *__temp_1063 }
> 854 | end
> 855 |
> 856 | default_stub private def max_by_internal(
> 857 |
> 858 |
> 859 | & : (T -> U)
> 860 | ) forall U
> 861 | super { |*__temp_1063| yield *__temp_1063 }
> 862 | end
> 863 |
> 864 | default_stub def max_of(
> 865 |
> 866 |
> 867 | & : (T -> U)
> 868 | ) : U forall U
> 869 | super { |*__temp_1063| yield *__temp_1063 }
> 870 | end
> 871 |
> 872 | default_stub def max_of?(
> 873 |
> 874 |
> 875 | & : (T -> U)
> 876 | ) : U | ::Nil forall U
> 877 | super { |*__temp_1063| yield *__temp_1063 }
> 878 | end
> 879 |
> 880 | default_stub private def max_of_internal(
> 881 |
> 882 |
> 883 | & : (T -> U)
> 884 | ) forall U
> 885 | super { |*__temp_1063| yield *__temp_1063 }
> 886 | end
> 887 |
> 888 | default_stub def min(
> 889 |
> 890 |
> 891 |
> 892 | ) : T
> 893 | super
> 894 | end
> 895 |
> 896 | default_stub def min?(
> 897 |
> 898 |
> 899 |
> 900 | ) : T | ::Nil
> 901 | super
> 902 | end
> 903 |
> 904 | default_stub def min_by(
> 905 |
> 906 |
> 907 | & : (T -> U)
> 908 | ) : T forall U
> 909 | super { |*__temp_1063| yield *__temp_1063 }
> 910 | end
> 911 |
> 912 | default_stub def min_by?(
> 913 |
> 914 |
> 915 | & : (T -> U)
> 916 | ) : T | ::Nil forall U
> 917 | super { |*__temp_1063| yield *__temp_1063 }
> 918 | end
> 919 |
> 920 | default_stub private def min_by_internal(
> 921 |
> 922 |
> 923 | & : (T -> U)
> 924 | ) forall U
> 925 | super { |*__temp_1063| yield *__temp_1063 }
> 926 | end
> 927 |
> 928 | default_stub def min_of(
> 929 |
> 930 |
> 931 | & : (T -> U)
> 932 | ) : U forall U
> 933 | super { |*__temp_1063| yield *__temp_1063 }
> 934 | end
> 935 |
> 936 | default_stub def min_of?(
> 937 |
> 938 |
> 939 | & : (T -> U)
> 940 | ) : U | ::Nil forall U
> 941 | super { |*__temp_1063| yield *__temp_1063 }
> 942 | end
> 943 |
> 944 | default_stub private def min_of_internal(
> 945 |
> 946 |
> 947 | & : (T -> U)
> 948 | ) forall U
> 949 | super { |*__temp_1063| yield *__temp_1063 }
> 950 | end
> 951 |
> 952 | default_stub def minmax(
> 953 |
> 954 |
> 955 |
> 956 | ) : ::Tuple(T, T)
> 957 | super
> 958 | end
> 959 |
> 960 | default_stub def minmax?(
> 961 |
> 962 |
> 963 |
> 964 | ) : ::Tuple(T | ::Nil, T | ::Nil)
> 965 | super
> 966 | end
> 967 |
> 968 | default_stub def minmax_by(
> 969 |
> 970 |
> 971 | & : (T -> U)
> 972 | ) : ::Tuple(T, T) forall U
> 973 | super { |*__temp_1063| yield *__temp_1063 }
> 974 | end
> 975 |
> 976 | default_stub def minmax_by?(
> 977 |
> 978 |
> 979 | & : (T -> U)
> 980 | ) : ::Tuple(T, T) | ::Tuple(Nil, Nil) forall U
> 981 | super { |*__temp_1063| yield *__temp_1063 }
> 982 | end
> 983 |
> 984 | default_stub private def minmax_by_internal(
> 985 |
> 986 |
> 987 | & : (T -> U)
> 988 | ) forall U
> 989 | super { |*__temp_1063| yield *__temp_1063 }
> 990 | end
> 991 |
> 992 | default_stub def minmax_of(
> 993 |
> 994 |
> 995 | & : (T -> U)
> 996 | ) : ::Tuple(U, U) forall U
> 997 | super { |*__temp_1063| yield *__temp_1063 }
> 998 | end
> 999 |
> 1000 | default_stub def minmax_of?(
> 1001 |
> 1002 |
> 1003 | & : (T -> U)
> 1004 | ) : ::Tuple(U, U) | ::Tuple(Nil, Nil) forall U
> 1005 | super { |*__temp_1063| yield *__temp_1063 }
> 1006 | end
> 1007 |
> 1008 | default_stub private def minmax_of_internal(
> 1009 |
> 1010 |
> 1011 | & : (T -> U)
> 1012 | ) forall U
> 1013 | super { |*__temp_1063| yield *__temp_1063 }
> 1014 | end
> 1015 |
> 1016 | default_stub private def compare_or_raise(
> 1017 | value, memo,
> 1018 |
> 1019 |
> 1020 | )
> 1021 | super
> 1022 | end
> 1023 |
> 1024 | default_stub def none?(
> 1025 |
> 1026 |
> 1027 | & : (T ->)
> 1028 | ) : Bool
> 1029 | super { |*__temp_1063| yield *__temp_1063 }
> 1030 | end
> 1031 |
> 1032 | default_stub def none?(
> 1033 | pattern,
> 1034 |
> 1035 |
> 1036 | ) : Bool
> 1037 | super
> 1038 | end
> 1039 |
> 1040 | default_stub def none?(
> 1041 |
> 1042 |
> 1043 |
> 1044 | ) : Bool
> 1045 | super
> 1046 | end
> 1047 |
> 1048 | default_stub def one?(
> 1049 |
> 1050 |
> 1051 | & : (T ->)
> 1052 | ) : Bool
> 1053 | super { |*__temp_1063| yield *__temp_1063 }
> 1054 | end
> 1055 |
> 1056 | default_stub def one?(
> 1057 | pattern,
> 1058 |
> 1059 |
> 1060 | ) : Bool
> 1061 | super
> 1062 | end
> 1063 |
> 1064 | default_stub def one?(
> 1065 |
> 1066 |
> 1067 |
> 1068 | ) : Bool
> 1069 | super
> 1070 | end
> 1071 |
> 1072 | default_stub def partition(
> 1073 |
> 1074 |
> 1075 | & : (T ->)
> 1076 | ) : ::Tuple(Array(T), Array(T))
> 1077 | super { |*__temp_1063| yield *__temp_1063 }
> 1078 | end
> 1079 |
> 1080 | default_stub def reject(
> 1081 |
> 1082 |
> 1083 | & : (T ->)
> 1084 | )
> 1085 | super { |*__temp_1063| yield *__temp_1063 }
> 1086 | end
> 1087 |
> 1088 | default_stub def reject(
> 1089 | type : U.class,
> 1090 |
> 1091 |
> 1092 | ) forall U
> 1093 | super
> 1094 | end
> 1095 |
> 1096 | default_stub def reject(
> 1097 | pattern,
> 1098 |
> 1099 |
> 1100 | ) : Array(T)
> 1101 | super
> 1102 | end
> 1103 |
> 1104 | default_stub def sample(
> 1105 | n : Int, random = Random::DEFAULT,
> 1106 |
> 1107 |
> 1108 | ) : Array(T)
> 1109 | super
> 1110 | end
> 1111 |
> 1112 | default_stub def sample(
> 1113 | random = Random::DEFAULT,
> 1114 |
> 1115 |
> 1116 | ) : T
> 1117 | super
> 1118 | end
> 1119 |
> 1120 | default_stub def select(
> 1121 |
> 1122 |
> 1123 | & : (T ->)
> 1124 | )
> 1125 | super { |*__temp_1063| yield *__temp_1063 }
> 1126 | end
> 1127 |
> 1128 | default_stub def select(
> 1129 | type : U.class,
> 1130 |
> 1131 |
> 1132 | ) : Array(U) forall U
> 1133 | super
> 1134 | end
> 1135 |
> 1136 | default_stub def select(
> 1137 | pattern,
> 1138 |
> 1139 |
> 1140 | ) : Array(T)
> 1141 | super
> 1142 | end
> 1143 |
> 1144 | default_stub def size(
> 1145 |
> 1146 |
> 1147 |
> 1148 | ) : Int32
> 1149 | super
> 1150 | end
> 1151 |
> 1152 | default_stub def empty?(
> 1153 |
> 1154 |
> 1155 |
> 1156 | ) : Bool
> 1157 | super
> 1158 | end
> 1159 |
> 1160 | default_stub def skip(
> 1161 | count : Int,
> 1162 |
> 1163 |
> 1164 | )
> 1165 | super
> 1166 | end
> 1167 |
> 1168 | default_stub def skip_while(
> 1169 |
> 1170 |
> 1171 | & : (T ->)
> 1172 | ) : Array(T)
> 1173 | super { |*__temp_1063| yield *__temp_1063 }
> 1174 | end
> 1175 |
> 1176 | default_stub def sum(
> 1177 | initial,
> 1178 |
> 1179 |
> 1180 | )
> 1181 | super
> 1182 | end
> 1183 |
> 1184 | default_stub def sum(
> 1185 |
> 1186 |
> 1187 |
> 1188 | )
> 1189 | super
> 1190 | end
> 1191 |
> 1192 | default_stub def sum(
> 1193 | initial,
> 1194 |
> 1195 | & : (T ->)
> 1196 | )
> 1197 | super { |*__temp_1063| yield *__temp_1063 }
> 1198 | end
> 1199 |
> 1200 | default_stub def sum(
> 1201 |
> 1202 |
> 1203 | & : (T ->)
> 1204 | )
> 1205 | super { |*__temp_1063| yield *__temp_1063 }
> 1206 | end
> 1207 |
> 1208 | default_stub private def additive_identity(
> 1209 | reflect,
> 1210 |
> 1211 |
> 1212 | )
> 1213 | super
> 1214 | end
> 1215 |
> 1216 | default_stub def product(
> 1217 | initial : Number,
> 1218 |
> 1219 |
> 1220 | )
> 1221 | super
> 1222 | end
> 1223 |
> 1224 | default_stub def product(
> 1225 |
> 1226 |
> 1227 |
> 1228 | )
> 1229 | super
> 1230 | end
> 1231 |
> 1232 | default_stub def product(
> 1233 | initial : Number,
> 1234 |
> 1235 | & : (T ->)
> 1236 | )
> 1237 | super { |*__temp_1063| yield *__temp_1063 }
> 1238 | end
> 1239 |
> 1240 | default_stub def product(
> 1241 |
> 1242 |
> 1243 | & : (T -> _)
> 1244 | )
> 1245 | super { |*__temp_1063| yield *__temp_1063 }
> 1246 | end
> 1247 |
> 1248 | default_stub def take_while(
> 1249 |
> 1250 |
> 1251 | & : (T ->)
> 1252 | ) : Array(T)
> 1253 | super { |*__temp_1063| yield *__temp_1063 }
> 1254 | end
> 1255 |
> 1256 | default_stub def tally_by(
> 1257 | hash,
> 1258 |
> 1259 | &
> 1260 | )
> 1261 | super { |*__temp_1063| yield *__temp_1063 }
> 1262 | end
> 1263 |
> 1264 | default_stub def tally_by(
> 1265 |
> 1266 |
> 1267 | &block : (T -> U)
> 1268 | ) : Hash(U, Int32) forall U
> 1269 | super { |*__temp_1063| yield *__temp_1063 }
> 1270 | end
> 1271 |
> 1272 | default_stub def tally(
> 1273 | hash,
> 1274 |
> 1275 |
> 1276 | )
> 1277 | super
> 1278 | end
> 1279 |
> 1280 | default_stub def tally(
> 1281 |
> 1282 |
> 1283 |
> 1284 | ) : Hash(T, Int32)
> 1285 | super
> 1286 | end
> 1287 |
> 1288 | default_stub def to_a(
> 1289 |
> 1290 |
> 1291 |
> 1292 | )
> 1293 | super
> 1294 | end
> 1295 |
> 1296 | default_stub def to_h(
> 1297 |
> 1298 |
> 1299 |
> 1300 | )
> 1301 | super
> 1302 | end
> 1303 |
> 1304 | default_stub def to_h(
> 1305 |
> 1306 |
> 1307 | & : (T -> Tuple(K, V))
> 1308 | ) forall K, V
> 1309 | super { |*__temp_1063| yield *__temp_1063 }
> 1310 | end
> 1311 |
> 1312 | default_stub def zip(
> 1313 | *others : Indexable | Iterable | Iterator,
> 1314 |
> 1315 | &
> 1316 | )
> 1317 | super { |*__temp_1063| yield *__temp_1063 }
> 1318 | end
> 1319 |
> 1320 | default_stub def zip(
> 1321 | *others : Indexable | Iterable | Iterator,
> 1322 |
> 1323 |
> 1324 | )
> 1325 | super
> 1326 | end
> 1327 |
> 1328 | default_stub def zip?(
> 1329 | *others : Indexable | Iterable | Iterator,
> 1330 |
> 1331 | &
> 1332 | )
> 1333 | super { |*__temp_1063| yield *__temp_1063 }
> 1334 | end
> 1335 |
> 1336 | default_stub def zip?(
> 1337 | *others : Indexable | Iterable | Iterator,
> 1338 |
> 1339 |
> 1340 | )
> 1341 | super
> 1342 | end
> 1343 |
> 1344 | default_stub def to_set(
> 1345 |
> 1346 |
> 1347 |
> 1348 | ) : Set(T)
> 1349 | super
> 1350 | end
> 1351 |
> 1352 |
> 1353 |
> 1354 | default_stub def self.zip(
> 1355 | main, others : U,
> 1356 |
> 1357 | &
> 1358 | ) forall U
> 1359 | super { |*__temp_1063| yield *__temp_1063 }
> 1360 | end
> 1361 |
> 1362 | default_stub def self.zip?(
> 1363 | main, others : U,
> 1364 |
> 1365 | &
> 1366 | ) forall U
> 1367 | super { |*__temp_1063| yield *__temp_1063 }
> 1368 | end
> 1369 |
> 1370 | default_stub def self.element_type(
> 1371 | x,
> 1372 |
> 1373 |
> 1374 | )
> 1375 | super
> 1376 | end
> 1377 |
> 1378 |
> 1379 |
> 1380 |
> 1381 | default_stub def byte_set(
> 1382 |
> 1383 |
> 1384 |
> 1385 | ) : Set(Int32)
> 1386 |
> 1387 | super
> 1388 | end
> 1389 |
> 1390 |
> 1391 | default_stub def char_set(
> 1392 |
> 1393 |
> 1394 |
> 1395 | ) : Set(Char)
> 1396 |
> 1397 | super
> 1398 | end
> 1399 |
> 1400 |
> 1401 | default_stub def bytes(
> 1402 |
> 1403 |
> 1404 |
> 1405 | ) : Array(Int32)
> 1406 |
> 1407 | super
> 1408 | end
> 1409 |
> 1410 |
> 1411 | default_stub def chars(
> 1412 |
> 1413 |
> 1414 |
> 1415 | ) : Array(Char)
> 1416 |
> 1417 | super
> 1418 | end
> 1419 |
> 1420 |
> 1421 | default_stub def <<(
> 1422 | value : Char,
> 1423 |
> 1424 |
> 1425 | ) : CharSet
> 1426 |
> 1427 | super
> 1428 | end
> 1429 |
> 1430 |
> 1431 | default_stub def <<(
> 1432 | value : UInt8,
> 1433 |
> 1434 |
> 1435 | ) : CharSet
> 1436 |
> 1437 | super
> 1438 | end
> 1439 |
> 1440 |
> 1441 | default_stub def <<(
> 1442 | value : Int32,
> 1443 |
> 1444 |
> 1445 | ) : CharSet
> 1446 |
> 1447 | super
> 1448 | end
> 1449 |
> 1450 |
> 1451 | default_stub def includes_byte?(
> 1452 | byte : UInt8,
> 1453 |
> 1454 |
> 1455 | ) : Bool
> 1456 |
> 1457 | super
> 1458 | end
> 1459 |
> 1460 |
> 1461 | default_stub def includes_byte?(
> 1462 | byte : Int32,
> 1463 |
> 1464 |
> 1465 | ) : Bool
> 1466 |
> 1467 | super
> 1468 | end
> 1469 |
> 1470 |
> 1471 | default_stub def includes_char?(
> 1472 | char : Char,
> 1473 |
> 1474 |
> 1475 | ) : Bool
> 1476 |
> 1477 | super
> 1478 | end
> 1479 |
> 1480 |
> 1481 | default_stub def includes?(
> 1482 | byte : UInt8 | Int32,
> 1483 |
> 1484 |
> 1485 | ) : Bool
> 1486 |
> 1487 | super
> 1488 | end
> 1489 |
> 1490 |
> 1491 | default_stub def includes?(
> 1492 | char : Char,
> 1493 |
> 1494 |
> 1495 | ) : Bool
> 1496 |
> 1497 | super
> 1498 | end
> 1499 |
> 1500 |
> 1501 | default_stub def each_byte(
> 1502 |
> 1503 |
> 1504 | &block : (Int32 ->)
> 1505 | )
> 1506 |
> 1507 | super { |*__temp_1063| yield *__temp_1063 }
> 1508 | end
> 1509 |
> 1510 |
> 1511 | default_stub def each_char(
> 1512 |
> 1513 |
> 1514 | &block : (Char ->)
> 1515 | )
> 1516 |
> 1517 | super { |*__temp_1063| yield *__temp_1063 }
> 1518 | end
> 1519 |
> 1520 |
> 1521 | default_stub def each(
> 1522 | *args,
> 1523 | **options,
> 1524 |
> 1525 | )
> 1526 |
> 1527 | super
> 1528 | end
> 1529 |
> 1530 |
> 1531 | default_stub def each(
> 1532 | *args,
> 1533 | **options,
> 1534 | &
> 1535 | )
> 1536 |
> 1537 | super { |*__temp_1063| yield *__temp_1063 }
> 1538 | end
> 1539 |
> 1540 |
> 1541 | default_stub def select_bytes(
> 1542 |
> 1543 |
> 1544 | &block : (Int32 -> Bool)
> 1545 | ) : Array(Int32)
> 1546 |
> 1547 | super { |*__temp_1063| yield *__temp_1063 }
> 1548 | end
> 1549 |
> 1550 |
> 1551 | default_stub def select_chars(
> 1552 |
> 1553 |
> 1554 | &block : (Char -> Bool)
> 1555 | ) : Array(Char)
> 1556 |
> 1557 | super { |*__temp_1063| yield *__temp_1063 }
> 1558 | end
> 1559 |
> 1560 |
> 1561 | default_stub def select(
> 1562 | *args,
> 1563 | **options,
> 1564 |
> 1565 | )
> 1566 |
> 1567 | super
> 1568 | end
> 1569 |
> 1570 |
> 1571 | default_stub def select(
> 1572 | *args,
> 1573 | **options,
> 1574 | &
> 1575 | )
> 1576 |
> 1577 | super { |*__temp_1063| yield *__temp_1063 }
> 1578 | end
> 1579 |
> 1580 |
> 1581 | default_stub def map_bytes(
> 1582 |
> 1583 |
> 1584 | &block : (Int32 -> T)
> 1585 | ) : Array(T) forall T
> 1586 |
> 1587 | super { |*__temp_1063| yield *__temp_1063 }
> 1588 | end
> 1589 |
> 1590 |
> 1591 | default_stub def map_chars(
> 1592 |
> 1593 |
> 1594 | &block : (Char -> T)
> 1595 | ) : Array(T) forall T
> 1596 |
> 1597 | super { |*__temp_1063| yield *__temp_1063 }
> 1598 | end
> 1599 |
> 1600 |
> 1601 | default_stub def map(
> 1602 | *args,
> 1603 | **options,
> 1604 |
> 1605 | )
> 1606 |
> 1607 | super
> 1608 | end
> 1609 |
> 1610 |
> 1611 | default_stub def map(
> 1612 | *args,
> 1613 | **options,
> 1614 | &
> 1615 | )
> 1616 |
> 1617 | super { |*__temp_1063| yield *__temp_1063 }
> 1618 | end
> 1619 |
> 1620 |
> 1621 | default_stub def random_byte(
> 1622 | random = Random::DEFAULT,
> 1623 |
> 1624 |
> 1625 | ) : Int32
> 1626 |
> 1627 | super
> 1628 | end
> 1629 |
> 1630 |
> 1631 | default_stub def random_char(
> 1632 | random = Random::DEFAULT,
> 1633 |
> 1634 |
> 1635 | ) : Char
> 1636 |
> 1637 | super
> 1638 | end
> 1639 |
> 1640 |
> 1641 | default_stub def each_random_byte(
> 1642 | n : Int,
> 1643 |
> 1644 | &block : (Int32 ->)
> 1645 | )
> 1646 |
> 1647 | super { |*__temp_1063| yield *__temp_1063 }
> 1648 | end
> 1649 |
> 1650 |
> 1651 | default_stub def each_random_char(
> 1652 | n : Int,
> 1653 |
> 1654 | &block : (Char ->)
> 1655 | )
> 1656 |
> 1657 | super { |*__temp_1063| yield *__temp_1063 }
> 1658 | end
> 1659 |
> 1660 |
> 1661 | default_stub def random_bytes(
> 1662 | length : Int, random = Random::DEFAULT,
> 1663 |
> 1664 |
> 1665 | ) : Array(Int32)
> 1666 |
> 1667 | super
> 1668 | end
> 1669 |
> 1670 |
> 1671 | default_stub def random_bytes(
> 1672 | lengths : Array(Int) | Range(Int, Int), random = Random::DEFAULT,
> 1673 |
> 1674 |
> 1675 | ) : Array(Int32)
> 1676 |
> 1677 | super
> 1678 | end
> 1679 |
> 1680 |
> 1681 | default_stub def random_chars(
> 1682 | length : Int, random = Random::DEFAULT,
> 1683 |
> 1684 |
> 1685 | ) : Array(Char)
> 1686 |
> 1687 | super
> 1688 | end
> 1689 |
> 1690 |
> 1691 | default_stub def random_chars(
> 1692 | lengths : Array(Int) | Range(Int, Int), random = Random::DEFAULT,
> 1693 |
> 1694 |
> 1695 | ) : Array(Char)
> 1696 |
> 1697 | super
> 1698 | end
> 1699 |
> 1700 |
> 1701 | default_stub def random_string(
> 1702 | length : Int | Array(Int) | Range(Int, Int),
> 1703 |
> 1704 |
> 1705 | ) : String
> 1706 |
> 1707 | super
> 1708 | end
> 1709 |
> 1710 |
> 1711 | default_stub def random_distinct_bytes(
> 1712 | length : Int,
> 1713 |
> 1714 |
> 1715 | ) : Array(Int32)
> 1716 |
> 1717 | super
> 1718 | end
> 1719 |
> 1720 |
> 1721 | default_stub def random_distinct_bytes(
> 1722 | length : Array(Int), random = Random::DEFAULT,
> 1723 |
> 1724 |
> 1725 | ) : Array(Int32)
> 1726 |
> 1727 | super
> 1728 | end
> 1729 |
> 1730 |
> 1731 | default_stub def random_distinct_bytes(
> 1732 | length : Range(Int, Int),
> 1733 |
> 1734 |
> 1735 | ) : Array(Int32)
> 1736 |
> 1737 | super
> 1738 | end
> 1739 |
> 1740 |
> 1741 | default_stub def random_distinct_chars(
> 1742 | length : Int,
> 1743 |
> 1744 |
> 1745 | ) : Array(Char)
> 1746 |
> 1747 | super
> 1748 | end
> 1749 |
> 1750 |
> 1751 | default_stub def random_distinct_chars(
> 1752 | length : Array(Int), random = Random::DEFAULT,
> 1753 |
> 1754 |
> 1755 | ) : Array(Char)
> 1756 |
> 1757 | super
> 1758 | end
> 1759 |
> 1760 |
> 1761 | default_stub def random_distinct_chars(
> 1762 | length : Range(Int, Int),
> 1763 |
> 1764 |
> 1765 | ) : Array(Char)
> 1766 |
> 1767 | super
> 1768 | end
> 1769 |
> 1770 |
> 1771 | default_stub def random_distinct_string(
> 1772 | length : Int | Array(Int) | Range(Int, Int),
> 1773 |
> 1774 |
> 1775 | ) : String
> 1776 |
> 1777 | super
> 1778 | end
> 1779 |
> 1780 |
> 1781 | default_stub def each_substring_with_index(
> 1782 | data : String, min_length : Int = 4,
> 1783 |
> 1784 | &block : (String, Int32 ->)
> 1785 | )
> 1786 |
> 1787 | super { |*__temp_1063| yield *__temp_1063 }
> 1788 | end
> 1789 |
> 1790 |
> 1791 | default_stub def substrings_with_indexes(
> 1792 | data : String, min_length : Int = 4,
> 1793 |
> 1794 |
> 1795 | ) : Array(Tuple(String, Int32))
> 1796 |
> 1797 | super
> 1798 | end
> 1799 |
> 1800 |
> 1801 | default_stub def each_substring(
> 1802 | data : String, min_length : Int = 4,
> 1803 |
> 1804 | &block : (String ->)
> 1805 | )
> 1806 |
> 1807 | super { |*__temp_1063| yield *__temp_1063 }
> 1808 | end
> 1809 |
> 1810 |
> 1811 | default_stub def substrings(
> 1812 | data : String, min_length : Int = 4,
> 1813 |
> 1814 |
> 1815 | ) : Array(String)
> 1816 |
> 1817 | super
> 1818 | end
> 1819 |
> 1820 |
> 1821 | default_stub def |(
> 1822 | other : CharSet,
> 1823 |
> 1824 |
> 1825 | ) : CharSet
> 1826 |
> 1827 | super
> 1828 | end
> 1829 |
> 1830 |
> 1831 | default_stub def +(
> 1832 | other : CharSet,
> 1833 |
> 1834 |
> 1835 | ) : CharSet
> 1836 |
> 1837 | super
> 1838 | end
> 1839 |
> 1840 |
> 1841 | default_stub def -(
> 1842 | other : CharSet,
> 1843 |
> 1844 |
> 1845 | ) : CharSet
> 1846 |
> 1847 | super
> 1848 | end
> 1849 |
> 1850 |
> 1851 | default_stub def &(
> 1852 | other : CharSet,
> 1853 |
> 1854 |
> 1855 | ) : CharSet
> 1856 |
> 1857 | super
> 1858 | end
> 1859 |
> 1860 |
> 1861 | default_stub def intersects?(
> 1862 | other : CharSet,
> 1863 |
> 1864 |
> 1865 | ) : Bool
> 1866 |
> 1867 | super
> 1868 | end
> 1869 |
> 1870 |
> 1871 | default_stub def subset_of?(
> 1872 | other : CharSet,
> 1873 |
> 1874 |
> 1875 | ) : Bool
> 1876 |
> 1877 | super
> 1878 | end
> 1879 |
> 1880 |
> 1881 | default_stub def ==(
> 1882 | other : CharSet,
> 1883 |
> 1884 |
> 1885 | ) : Bool
> 1886 |
> 1887 | super
> 1888 | end
> 1889 |
> 1890 |
> 1891 | default_stub def ===(
> 1892 | other : String,
> 1893 |
> 1894 |
> 1895 | ) : Bool
> 1896 |
> 1897 | super
> 1898 | end
> 1899 |
> 1900 |
> 1901 | default_stub def =~(
> 1902 | string : String,
> 1903 |
> 1904 |
> 1905 | )
> 1906 |
> 1907 | super
> 1908 | end
> 1909 |
> 1910 |
> 1911 | default_stub def to_a(
> 1912 | *args,
> 1913 | **options,
> 1914 |
> 1915 | )
> 1916 |
> 1917 | super
> 1918 | end
> 1919 |
> 1920 |
> 1921 | default_stub def to_a(
> 1922 | *args,
> 1923 | **options,
> 1924 | &
> 1925 | )
> 1926 |
> 1927 | super { |*__temp_1063| yield *__temp_1063 }
> 1928 | end
> 1929 |
> 1930 |
> 1931 | default_stub def to_set(
> 1932 | *args,
> 1933 | **options,
> 1934 |
> 1935 | )
> 1936 |
> 1937 | super
> 1938 | end
> 1939 |
> 1940 |
> 1941 | default_stub def to_set(
> 1942 | *args,
> 1943 | **options,
> 1944 | &
> 1945 | )
> 1946 |
> 1947 | super { |*__temp_1063| yield *__temp_1063 }
> 1948 | end
> 1949 |
> 1950 |
> 1951 |
> 1952 |
> 1953 | default_stub def self.new(
> 1954 | values : Enumerable(UInt8 | Int32 | Char),
> 1955 |
> 1956 |
> 1957 | )
> 1958 | super
> 1959 | end
> 1960 |
> 1961 | default_stub def self.new(
> 1962 | values : Indexable(UInt8 | Int32 | Char),
> 1963 |
> 1964 |
> 1965 | )
> 1966 | super
> 1967 | end
> 1968 |
> 1969 | default_stub def self.new(
> 1970 |
> 1971 |
> 1972 |
> 1973 | )
> 1974 | super
> 1975 | end
> 1976 |
> 1977 | default_stub def self.[](
> 1978 | *values : UInt8 | Int32 | Char | Range(UInt8, UInt8) | Range(Int32, Int32) | Range(Char, Char),
> 1979 |
> 1980 |
> 1981 | ) : CharSet
> 1982 | super
> 1983 | end
> 1984 |
> 1985 |
Error: expanding macro
In lib/spectator/src/spectator/mocks/stubbable.cr:161:58
161 | {{ if method.return_type && method.return_type.resolve == NoReturn
^------
Error: undefined constant U
In /var/lib/snapd/snap/crystal/1384/share/crystal/src/enumerable.cr:466:48
466 | def each_with_object(obj : U, & : T, U ->) : U forall U
^
Error: undefined constant U
I have this test that fails:
let(nodes) do
[
Node.new("<div></div>"),
Node.new("<div></div>"),
Node.new("<div></div>")
]
end
it "contains valid nodes" do
expect(described_class.new(nodes).to_a).to contain_exactly nodes
end
There the output:
Failures:
1) Katana::Html::NodeCollection#initialize contains the passed elements
Failure: (described_class.new(nodes)).to_a does not contain exactly {nodes}
expected: [[Katana::Html::Node(@is_text=false, @origin=#<XML::HTMLDocument:0x10c693f00 children=[#<XML::Element:0x10c685e00 name="div">]>), Katana::Html::Node(@is_text=false, @origin=#<XML::HTMLDocument:0x10c693e40 children=[#<XML::Element:0x10c685d80 name="div">]>), Katana::Html::Node(@is_text=false, @origin=#<XML::HTMLDocument:0x10c693d80 children=[#<XML::Element:0x10c685d00 name="div">]>)]]
actual: [Katana::Html::Node(@is_text=false, @origin=#<XML::HTMLDocument:0x10c693f00 children=[#<XML::Element:0x10c685e00 name="div">]>), Katana::Html::Node(@is_text=false, @origin=#<XML::HTMLDocument:0x10c693e40 children=[#<XML::Element:0x10c685d80 name="div">]>), Katana::Html::Node(@is_text=false, @origin=#<XML::HTMLDocument:0x10c693d80 children=[#<XML::Element:0x10c685d00 name="div">]>)]
missing: [Katana::Html::Node(@is_text=false, @origin=#<XML::HTMLDocument:0x10c693f00 children=[#<XML::Element:0x10c685e00 name="div">]>), Katana::Html::Node(@is_text=false, @origin=#<XML::HTMLDocument:0x10c693e40 children=[#<XML::Element:0x10c685d80 name="div">]>), Katana::Html::Node(@is_text=false, @origin=#<XML::HTMLDocument:0x10c693d80 children=[#<XML::Element:0x10c685d00 name="div">]>)]
extra: [Katana::Html::Node(@is_text=false, @origin=#<XML::HTMLDocument:0x10c693f00 children=[#<XML::Element:0x10c685e00 name="div">]>), Katana::Html::Node(@is_text=false, @origin=#<XML::HTMLDocument:0x10c693e40 children=[#<XML::Element:0x10c685d80 name="div">]>), Katana::Html::Node(@is_text=false, @origin=#<XML::HTMLDocument:0x10c693d80 children=[#<XML::Element:0x10c685d00 name="div">]>)]
It seems the matcher cannot match the objects and for the test to pass, I need to write it like this:
it "contains valid nodes" do
described_class.new(nodes).to_a.each_with_index do |node, i|
expect(node).to eq nodes[i]
end
end
If you attempt to do something like it "sets #{my_var} on object"
you get an ugly compiler error like
$ crystal spec spec/i18next_plurals_spec.cr
Showing last frame. Use --error-trace for full trace.
There was a problem expanding macro '_spectator_metadata'
Code in macro 'it'
4 | _spectator_metadata(__temp_36, :metadata, )
^
Called macro defined in lib/spectator/src/spectator/dsl/metadata.cr:6:13
6 | private macro _spectator_metadata(name, source, *tags, **metadata)
Which expanded to:
> 1 | private def self.__temp_36
^--------
Error: can't declare def dynamically
Would it be possible to have a more helpful error for this? You can check for string interpolation in macros by doing something like {% if string.is_a?(StringInterpolation) %}
When porting RSpec specs to Spectator, often times the doubles lack any body or stubs.
let(:foo) { double(:foo) }
I think it would be useful to support defining doubles without a block:
double :foo
double :bar
let(args) { {double(:foo), double(:bar)} }
After upgrading to spectator 0.10 I noticed that the stub method(arg1, ...)
syntax is now being confused as a method call.
Trying stub method(arg1,arg2) { true }
gives me the same error, as well.
require "./spec_helper"
require "../src/command_kit/man"
Spectator.describe CommandKit::Man do
module TestMan
class TestCommand
include CommandKit::Man
end
end
let(command_class) { TestMan::TestCommand }
subject { command_class.new }
describe "#man" do
mock TestMan::TestCommand do
stub system(command, arg)
end
let(man_page) { "foo" }
it "must call system() with the given man page" do
expect(subject).to receive(:system).with("man", [man_page])
subject.man(man_page)
end
context "when given the section: keyword argument" do
let(section) { 7 }
it "must call system() with the given section number and man page" do
expect(subject).to receive(:system).with("man", [section.to_s, man_page])
subject.man(man_page, section: section)
end
end
end
end
crystal spec spec/man_spec.cr --error-trace
error in line 1
Error: while requiring "./spec/man_spec.cr"
In spec/man_spec.cr:4:11
4 | Spectator.describe CommandKit::Man do
^-------
Error: expanding macro
In spec/man_spec.cr:4:1
4 | Spectator.describe CommandKit::Man do
^
Error: expanding macro
There was a problem expanding macro 'describe'
Called macro defined in macro 'macro_140629716498624'
46 | macro describe(description, *tags, **metadata, &block)
Which expanded to:
> 1 | class ::SpectatorTestContext
> 2 | describe(CommandKit::Man, ) do
> 3 | module TestMan
> 4 | class TestCommand
> 5 | include CommandKit::Man
> 6 | end
> 7 | end
> 8 | let(command_class) do
> 9 | TestMan::TestCommand
> 10 | end
> 11 | subject do
> 12 | command_class.new
> 13 | end
> 14 | describe("#man") do
> 15 | mock(TestMan::TestCommand) do
> 16 | stub(system(command, arg))
> 17 | end
> 18 | let(man_page) do
> 19 | "foo"
> 20 | end
> 21 | it("must call system() with the given man page") do
> 22 | (expect(subject)).to((receive(:system)).with("man", [man_page]))
> 23 | subject.man(man_page)
> 24 | end
> 25 | context("when given the section: keyword argument") do
> 26 | let(section) do
> 27 | 7
> 28 | end
> 29 | it("must call system() with the given section number and man page") do
> 30 | (expect(subject)).to((receive(:system)).with("man", [section.to_s, man_page]))
> 31 | subject.man(man_page, section: section)
> 32 | end
> 33 | end
> 34 | end
> 35 | end
> 36 | end
> 37 |
Error: expanding macro
In spec/man_spec.cr:15:1
15 | describe "#man" do
^-------
Error: expanding macro
In spec/man_spec.cr:22:1
22 | it "must call system() with the given man page" do
^-
Error: expanding macro
In spec/man_spec.cr:22:1
22 | it "must call system() with the given man page" do
^
Error: expanding macro
There was a problem expanding macro 'it'
Called macro defined in macro 'define_example'
17 | macro it(what = nil, *tags, **metadata, &block)
Which expanded to:
> 1 |
> 2 |
> 3 |
> 4 | _spectator_metadata(__temp_36, :metadata, )
> 5 | _spectator_metadata(__temp_67, __temp_36, )
> 6 |
> 7 |
> 8 |
> 9 |
> 10 | private def __temp_68() : Nil
> 11 | (expect(subject)).to((receive(:system)).with("man", [man_page]))
> 12 | subject.man(man_page)
> 13 |
> 14 | end
> 15 |
> 16 | ::Spectator::DSL::Builder.add_example(
> 17 | _spectator_example_name("must call system() with the given man page"),
> 18 | ::Spectator::Location.new("/data/home/postmodern/code/crystal/commit_kit.cr/spec/man_spec.cr", 22, 26),
> 19 | -> { new.as(::Spectator::Context) },
> 20 | __temp_67
> 21 | ) do |example|
> 22 | example.with_context(SpectatorTestContext::Group__temp_51::Group__temp_57) do
> 23 |
> 24 | __temp_68
> 25 |
> 26 | end
> 27 | end
> 28 |
> 29 |
> 30 |
Error: instantiating 'Spectator::Example#with_context(SpectatorTestContext::Group__temp_51::Group__temp_57.class)'
In spec/man_spec.cr:22:1
22 | it "must call system() with the given man page" do
^
Error: expanding macro
There was a problem expanding macro 'it'
Called macro defined in macro 'define_example'
17 | macro it(what = nil, *tags, **metadata, &block)
Which expanded to:
> 1 |
> 2 |
> 3 |
> 4 | _spectator_metadata(__temp_36, :metadata, )
> 5 | _spectator_metadata(__temp_67, __temp_36, )
> 6 |
> 7 |
> 8 |
> 9 |
> 10 | private def __temp_68() : Nil
> 11 | (expect(subject)).to((receive(:system)).with("man", [man_page]))
> 12 | subject.man(man_page)
> 13 |
> 14 | end
> 15 |
> 16 | ::Spectator::DSL::Builder.add_example(
> 17 | _spectator_example_name("must call system() with the given man page"),
> 18 | ::Spectator::Location.new("/data/home/postmodern/code/crystal/commit_kit.cr/spec/man_spec.cr", 22, 26),
> 19 | -> { new.as(::Spectator::Context) },
> 20 | __temp_67
> 21 | ) do |example|
> 22 | example.with_context(SpectatorTestContext::Group__temp_51::Group__temp_57) do
> 23 |
> 24 | __temp_68
> 25 |
> 26 | end
> 27 | end
> 28 |
> 29 |
> 30 |
Error: instantiating 'Spectator::Example#with_context(SpectatorTestContext::Group__temp_51::Group__temp_57.class)'
In spec/man_spec.cr:22:1
22 | it "must call system() with the given man page" do
^
Error: expanding macro
There was a problem expanding macro 'it'
Called macro defined in macro 'define_example'
17 | macro it(what = nil, *tags, **metadata, &block)
Which expanded to:
> 1 |
> 2 |
> 3 |
> 4 | _spectator_metadata(__temp_36, :metadata, )
> 5 | _spectator_metadata(__temp_67, __temp_36, )
> 6 |
> 7 |
> 8 |
> 9 |
> 10 | private def __temp_68() : Nil
> 11 | (expect(subject)).to((receive(:system)).with("man", [man_page]))
> 12 | subject.man(man_page)
> 13 |
> 14 | end
> 15 |
> 16 | ::Spectator::DSL::Builder.add_example(
> 17 | _spectator_example_name("must call system() with the given man page"),
> 18 | ::Spectator::Location.new("/data/home/postmodern/code/crystal/commit_kit.cr/spec/man_spec.cr", 22, 26),
> 19 | -> { new.as(::Spectator::Context) },
> 20 | __temp_67
> 21 | ) do |example|
> 22 | example.with_context(SpectatorTestContext::Group__temp_51::Group__temp_57) do
> 23 |
> 24 | __temp_68
> 25 |
> 26 | end
> 27 | end
> 28 |
> 29 |
> 30 |
Error: instantiating '__temp_68()'
In spec/man_spec.cr:24:9
24 |
^--
Error: instantiating 'SpectatorTestContext::Group__temp_51::TestMan::TestCommand#man(String)'
In src/command_kit/man.cr:36:9
36 | system("man", [section.to_s, page])
^-----
Error: instantiating 'system(String, Array(String))'
In spec/man_spec.cr:6:11
6 | class TestCommand
^
Error: expanding macro
There was a problem expanding macro 'stub'
Called macro defined in lib/spectator/src/spectator/mocks/stubs.cr:3:13
3 | private macro stub(definition, *types, _file = __FILE__, _line = __LINE__, return_type = :undefined, &block)
Which expanded to:
> 1 |
> 2 |
> 3 |
> 4 |
> 5 | def system(command, arg)
> 6 | if (__temp_60 = ::Spectator::Harness.current?)
> 7 | __temp_61 = ::Spectator::Mocks::GenericArguments.create(command, arg)
> 8 | __temp_62 = ::Spectator::Mocks::MethodCall.new(:system, __temp_61)
> 9 | __temp_60.mocks.record_call(self, __temp_62)
> 10 | if (__temp_63 = __temp_60.mocks.find_stub(self, __temp_62))
> 11 | return __temp_63.call!(__temp_61) { system }
> 12 | end
> 13 |
> 14 |
> 15 | system
> 16 |
> 17 | else
> 18 | system
> 19 | end
> 20 | end
> 21 |
> 22 | def system(command, arg)
> 23 | if (__temp_60 = ::Spectator::Harness.current?)
> 24 | __temp_61 = ::Spectator::Mocks::GenericArguments.create(command, arg)
> 25 | __temp_62 = ::Spectator::Mocks::MethodCall.new(:system, __temp_61)
> 26 | __temp_60.mocks.record_call(self, __temp_62)
> 27 | if (__temp_63 = __temp_60.mocks.find_stub(self, __temp_62))
> 28 | return __temp_63.call!(__temp_61) { system { |*__temp_64| yield *__temp_64 } }
> 29 | end
> 30 |
> 31 |
> 32 | system do |*__temp_65|
> 33 | yield *__temp_65
> 34 | end
> 35 |
> 36 | else
> 37 | system do |*__temp_65|
> 38 | yield *__temp_65
> 39 | end
> 40 | end
> 41 | end
> 42 |
Error: wrong number of arguments for 'SpectatorTestContext::Group__temp_51::TestMan::TestCommand#system' (given 0, expected 2)
Overloads are:
- SpectatorTestContext::Group__temp_51::TestMan::TestCommand#system(command, arg)
- SpectatorTestContext::Group__temp_51::TestMan::TestCommand#system(command, arg, &block)
development_dependencies:
spectator:
gitlab: arctic-fox/spectator
# branch: master
version: "~> 0.10"
Having a spec file
require "./spec_helper"
Spectator.describe Thing do
it "works" do
expect(false).to eq(true)
end
end
crystal spec
reports
Failure: false does not equal true
actual: false
expected: true
# spec/bisect_spec.cr:21
The example is defined at line 4 but (I suppose) after macro expansion it appears on line 21. This is a problem in case one uses tools like vim-test which run the spec-under-cursor using the line number of it
.
Can anything be done to make the line numbers match?
At the moment the order in which Spectator executes around + before/after hooks is:
before each
around each before
in the example
around each after
after each
When mixed with the other "each" and "all" hooks, around_each starts execution after before_each and finishes execution before after_each.
This makes it difficult to do things like:
describe "example" do
around_each do |proc|
default_state = described_class.save_state
proc.call
described_class.load_state(default_state)
end
context "with state1" do
before_each { described_class.load_state(state1) }
it โฆ
end
context "with state2" do
before_each { described_class.load_state(state2) }
it โฆ
end
end
Obviously there are work-arounds for the above, but I don't feel that they are as neat as using a combination of around + before/after hooks.
The order of execution also differs from the order in which Rspec executes the same hooks.
around each before
before each
in the example
after each
around each after
I was wondering if there is a reason for this difference and if it would be possible to bring this bit of functionality into sync with Rspec?
An interesting problem. I'm attempting to port this Ruby code which defines a Type
class which contains other classes such as Int32
. In Crystal I had to explicitly specify ::Int32
to avoid constant shadowing. However, Spectator is having issues with crystal_type_id
. It appears that when describing Hexdump::Type
the Int32
class contained within is shadowing the top level Int32
.
module Hexdump
#
# @api private
#
# @since 1.0.0
#
class Type
enum Endian
LITTLE
BIG
NETWORK = BIG
def self.native : Endian
int = 1_i32
native_bytes = Bytes.new(pointerof(int).as(UInt8 *), sizeof(typeof(int)))
big_endian_bytes = Bytes.new(sizeof(typeof(int)))
IO::ByteFormat::BigEndian.encode(int,big_endian_bytes)
if native_bytes == big_endian_bytes; BIG
else LITTLE
end
end
end
# The size in bytes of the type.
#
# @return [1, 2, 4, 8]
getter size : ::Int32
getter? signed : ::Bool
# The endian-ness of the type.
#
# @return [:little, :big, nil]
getter endian : Endian?
#
# Initializes the type.
#
# @param [Symbol] name
#
# @param [:little, :big, nil] endian
#
# @param [1, 2, 4, 8] size
#
# @param [Boolean] signed
#
# @raise [ArgumentError]
# Invalid `endian:` or `size:` values.
#
def initialize(@size : ::Int32, @signed : ::Bool, @endian : Endian? = nil)
end
#
# Whether the type is unsigned.
#
# @return [Boolean]
#
def unsigned?
!signed?
end
# The native endian-ness.
NATIVE_ENDIAN = Endian.native
#
# Represents a signed integer type.
#
class Int < self
#
# Initializes the int type.
#
# @param [:little, :big] endian (NATIVE_ENDIAN)
# The endian-ness of the int type.
#
def initialize(size : Int32, endian : Endian? = NATIVE_ENDIAN)
super(size: size, signed: true, endian: endian)
end
end
class Int32 < Int
#
# @see Int#initialize
#
def initialize(endian : Endian? = NATIVE_ENDIAN)
super(size: 4, endian: endian)
end
end
end
end
require "./spec_helper"
require "../src/hexdump/type"
Spectator.describe Hexdump::Type do
describe "#initialize" do
let(size) { 4 }
let(signed) { true }
subject { described_class.new(size: size, signed: signed) }
it "must set #size" do
expect(subject.size).to eq(size)
end
it "must not set #endian" do
expect(subject.endian).to be(nil)
end
it "must set #signed?" do
expect(subject.signed?).to eq(signed)
end
context "when given endian:" do
let(endian) { Hexdump::Type::Endian::BIG }
subject do
described_class.new(size: size, signed: signed, endian: endian)
end
it "must set #endian" do
expect(subject.endian).to eq(endian)
end
end
end
describe "#signed?" do
let(size) { 4 }
subject { described_class.new(size: size, signed: signed) }
context "when initialized with signed: true" do
let(signed) { true }
it do
expect(subject.signed?).to be(true)
end
end
context "when initialized with signed: false" do
let(signed) { false }
it do
expect(subject.signed?).to be(false)
end
end
end
describe "#unsigned?" do
let(size) { 4 }
subject { described_class.new(size: size, signed: signed) }
context "when initialized with signed: true" do
let(signed) { true }
it do
expect(subject.unsigned?).to be(false)
end
end
context "when initialized with signed: false" do
let(signed) { false }
it do
expect(subject.unsigned?).to be(true)
end
end
end
describe Hexdump::Type::Int32 do
it { expect(subject).is_a?(Hexdump::Type::Int) }
describe "#initialize" do
it "must default #signed? to true" do
expect(subject.signed?).to be(true)
end
it "must default #size to 4" do
expect(subject.size).to be(4)
end
it "must default #endian to NATIVE_ENDIAN" do
expect(subject.endian).to eq(Hexdump::Type::NATIVE_ENDIAN)
end
end
end
end
$ crystal spec spec/type_spec.cr --error-trace
error in line 1
Error: while requiring "./spec/type_spec.cr"
In spec/type_spec.cr:4:11
4 | Spectator.describe Hexdump::Type do
^-------
Error: expanding macro
In spec/type_spec.cr:4:1
4 | Spectator.describe Hexdump::Type do
^
Error: expanding macro
There was a problem expanding macro 'describe'
Called macro defined in macro 'macro_140623426311824'
46 | macro describe(description, *tags, **metadata, &block)
Which expanded to:
> 1 | class ::SpectatorTestContext
> 2 | describe(Hexdump::Type, ) do
> 3 | describe("#initialize") do
> 4 | let(size) do
> 5 | 4
> 6 | end
> 7 | let(signed) do
> 8 | true
> 9 | end
> 10 | subject do
> 11 | described_class.new(size: size, signed: signed)
> 12 | end
> 13 | it("must set #size") do
> 14 | (expect(subject.size)).to(eq(size))
> 15 | end
> 16 | it("must not set #endian") do
> 17 | (expect(subject.endian)).to(be(nil))
> 18 | end
> 19 | it("must set #signed?") do
> 20 | (expect(subject.signed?)).to(eq(signed))
> 21 | end
> 22 | context("when given endian:") do
> 23 | let(endian) do
> 24 | Hexdump::Type::Endian::BIG
> 25 | end
> 26 | subject do
> 27 | described_class.new(size: size, signed: signed, endian: endian)
> 28 | end
> 29 | it("must set #endian") do
> 30 | (expect(subject.endian)).to(eq(endian))
> 31 | end
> 32 | end
> 33 | end
> 34 | describe("#signed?") do
> 35 | let(size) do
> 36 | 4
> 37 | end
> 38 | subject do
> 39 | described_class.new(size: size, signed: signed)
> 40 | end
> 41 | context("when initialized with signed: true") do
> 42 | let(signed) do
> 43 | true
> 44 | end
> 45 | it do
> 46 | (expect(subject.signed?)).to(be(true))
> 47 | end
> 48 | end
> 49 | context("when initialized with signed: false") do
> 50 | let(signed) do
> 51 | false
> 52 | end
> 53 | it do
> 54 | (expect(subject.signed?)).to(be(false))
> 55 | end
> 56 | end
> 57 | end
> 58 | describe("#unsigned?") do
> 59 | let(size) do
> 60 | 4
> 61 | end
> 62 | subject do
> 63 | described_class.new(size: size, signed: signed)
> 64 | end
> 65 | context("when initialized with signed: true") do
> 66 | let(signed) do
> 67 | true
> 68 | end
> 69 | it do
> 70 | (expect(subject.unsigned?)).to(be(false))
> 71 | end
> 72 | end
> 73 | context("when initialized with signed: false") do
> 74 | let(signed) do
> 75 | false
> 76 | end
> 77 | it do
> 78 | (expect(subject.unsigned?)).to(be(true))
> 79 | end
> 80 | end
> 81 | end
> 82 | describe(Hexdump::Type::Int32) do
> 83 | it do
> 84 | expect(subject).is_a?(Hexdump::Type::Int)
> 85 | end
> 86 | describe("#initialize") do
> 87 | it("must default #signed? to true") do
> 88 | (expect(subject.signed?)).to(be(true))
> 89 | end
> 90 | it("must default #size to 4") do
> 91 | (expect(subject.size)).to(be(4))
> 92 | end
> 93 | it("must default #endian to NATIVE_ENDIAN") do
> 94 | (expect(subject.endian)).to(eq(Hexdump::Type::NATIVE_ENDIAN))
> 95 | end
> 96 | end
> 97 | end
> 98 | end
> 99 | end
> 100 |
Error: expanding macro
In spec/type_spec.cr:15:11
15 | it "must not set #endian" do
^-------
Error: expanding macro
In spec/type_spec.cr:14:1
14 |
^-
Error: expanding macro
In spec/type_spec.cr:14:1
14 |
^
Error: expanding macro
There was a problem expanding macro 'it'
Called macro defined in macro 'define_example'
17 | macro it(what = nil, *tags, **metadata, &block)
Which expanded to:
> 1 |
> 2 |
> 3 |
> 4 | _spectator_metadata(__temp_36, :metadata, )
> 5 | _spectator_metadata(__temp_65, __temp_36, )
> 6 |
> 7 |
> 8 |
> 9 |
> 10 | private def __temp_66() : Nil
> 11 | (expect(subject.endian)).to(be(nil))
> 12 | end
> 13 |
> 14 | ::Spectator::DSL::Builder.add_example(
> 15 | _spectator_example_name("must not set #endian"),
> 16 | ::Spectator::Location.new("/data/home/postmodern/code/crystal/hexdump.cr/spec/type_spec.cr", 15, 17),
> 17 | -> { new.as(::Spectator::Context) },
> 18 | __temp_65
> 19 | ) do |example|
> 20 | example.with_context(SpectatorTestContext::Group__temp_51::Group__temp_55) do
> 21 |
> 22 | __temp_66
> 23 |
> 24 | end
> 25 | end
> 26 |
> 27 |
> 28 |
Error: instantiating 'Spectator::Example#with_context(SpectatorTestContext::Group__temp_51::Group__temp_55.class)'
In spec/type_spec.cr:14:1
14 |
^
Error: expanding macro
There was a problem expanding macro 'it'
Called macro defined in macro 'define_example'
17 | macro it(what = nil, *tags, **metadata, &block)
Which expanded to:
> 1 |
> 2 |
> 3 |
> 4 | _spectator_metadata(__temp_36, :metadata, )
> 5 | _spectator_metadata(__temp_65, __temp_36, )
> 6 |
> 7 |
> 8 |
> 9 |
> 10 | private def __temp_66() : Nil
> 11 | (expect(subject.endian)).to(be(nil))
> 12 | end
> 13 |
> 14 | ::Spectator::DSL::Builder.add_example(
> 15 | _spectator_example_name("must not set #endian"),
> 16 | ::Spectator::Location.new("/data/home/postmodern/code/crystal/hexdump.cr/spec/type_spec.cr", 15, 17),
> 17 | -> { new.as(::Spectator::Context) },
> 18 | __temp_65
> 19 | ) do |example|
> 20 | example.with_context(SpectatorTestContext::Group__temp_51::Group__temp_55) do
> 21 |
> 22 | __temp_66
> 23 |
> 24 | end
> 25 | end
> 26 |
> 27 |
> 28 |
Error: instantiating 'Spectator::Example#with_context(SpectatorTestContext::Group__temp_51::Group__temp_55.class)'
In spec/type_spec.cr:14:1
14 |
^
Error: expanding macro
There was a problem expanding macro 'it'
Called macro defined in macro 'define_example'
17 | macro it(what = nil, *tags, **metadata, &block)
Which expanded to:
> 1 |
> 2 |
> 3 |
> 4 | _spectator_metadata(__temp_36, :metadata, )
> 5 | _spectator_metadata(__temp_65, __temp_36, )
> 6 |
> 7 |
> 8 |
> 9 |
> 10 | private def __temp_66() : Nil
> 11 | (expect(subject.endian)).to(be(nil))
> 12 | end
> 13 |
> 14 | ::Spectator::DSL::Builder.add_example(
> 15 | _spectator_example_name("must not set #endian"),
> 16 | ::Spectator::Location.new("/data/home/postmodern/code/crystal/hexdump.cr/spec/type_spec.cr", 15, 17),
> 17 | -> { new.as(::Spectator::Context) },
> 18 | __temp_65
> 19 | ) do |example|
> 20 | example.with_context(SpectatorTestContext::Group__temp_51::Group__temp_55) do
> 21 |
> 22 | __temp_66
> 23 |
> 24 | end
> 25 | end
> 26 |
> 27 |
> 28 |
Error: instantiating '__temp_66()'
In spec/type_spec.cr:16:31
16 | expect(subject.endian).to be(nil)
^-
Error: instantiating 'Spectator::Expectation::Target(Hexdump::Type::Endian | Nil)#to(Spectator::Matchers::ReferenceMatcher(Nil))'
In lib/spectator/src/spectator/expectation.cr:104:7
104 | def to(matcher, message = nil) : Nil
^-
Error: instantiating 'to(Spectator::Matchers::ReferenceMatcher(Nil), Nil)'
In lib/spectator/src/spectator/expectation.cr:105:30
105 | match_data = matcher.match(@expression)
^----
Error: instantiating 'Spectator::Matchers::ReferenceMatcher(Nil)#match(Spectator::Expression(Hexdump::Type::Endian | Nil)+)'
In lib/spectator/src/spectator/matchers/standard_matcher.cr:27:10
27 | if match?(actual)
^-----
Error: instantiating 'match?(Spectator::Expression(Hexdump::Type::Endian | Nil)+)'
In lib/spectator/src/spectator/matchers/reference_matcher.cr:21:28
21 | actual.value.class == value.class && actual.value == value
^
Error: instantiating '(Hexdump::Type::Endian.class | Nil.class)#==(Nil.class)'
In /var/lib/snapd/snap/crystal/793/share/crystal/src/class.cr:18:5
18 | crystal_type_id == other.crystal_type_id
^--------------
Error: instantiating 'crystal_type_id()'
In /var/lib/snapd/snap/crystal/793/share/crystal/src/primitives.cr:32:25
32 | def crystal_type_id : Int32
^----
Error: method Hexdump::Type::Endian.crystal_type_id must return Int32 but it is returning Hexdump::Type::Int32
Hexdump::Type::Int32 trace:
/var/lib/snapd/snap/crystal/793/share/crystal/src/primitives.cr:32
def crystal_type_id : Int32
It seems like you keep this repo and your GitLab repo in sync commit-wise, but there are different open issues in both. I'm about to open a GitHub PR but I have no idea if I should actually make it on GitLab instead.
Which repo is/should be the main one and which is a mirror? Could you add something to the README to help us understand?
It would be great, although I don't know if it's quite possible, to have allow
and receive
which tend to work closely together. This would allow for us to make sure that certain methods are being called as the result of another method being called. For instance:
allow(model).to receive(:fit_corpus).with(text)
it "calls its internal methods #fit_corpus to build the corpus obj" do
expect(model).to receive(:fit_corpus).with(text)
model.fit(text)
end
https://relishapp.com/rspec/rspec-mocks/v/2-14/docs/method-stubs
Hi there,
First of all, thank you so much for this project! I was never a great fun of the built in should
syntax or the way it mutates test objects, so it was awesome to find an option of using expect(โฆ).to โฆ
. The fact that it comes with all the familiar rspec-like goodies (eg: subject
, described_class
, let
, mocks & doubles, spies) is the icing on top of the cake! ๐
The only shortcoming I found so far is that there is no way to capture STDOUT / STDERR of the app that is being tested. This is not much of an issue when dealing with web apps, but it does become a problem when testing CLI apps:
start_with("Error: ")
I've built a simple "Hello World" to demonstrate the problem. If you checkout the project and run make tests
you'll see that the output produced by Spectator is:
$ crystal spec --progress --order rand
=> STDOUT Hello World
=> STDERR Hello World
.
Finished in 38 microseconds
1 examples, 0 failures, 0 errors, 0 pending
Randomized with seed 13940
Obviously, lines 2 & 3 of this output come from my hello-world app and not from Spectator itself.
Would it be possible to update Spectator to support a configuration that allows us to redirect STOUT of the test to one custom IO stream, and STDERR to another? Something like:
Spectator.configure do |config|
config.capture_stdout
config.capture_stderr
end
Spectator would then create a custom IO stream during a test run. This streams would capture the output of the app during the test. I guess it would be best if they were initialised anew for each test and destroyed when the test is done. This would allow us to test stdout and stderr outputs of our scripts using existing matchers, for example:
subject { MyApp.show_error_with_help }
it { expect(subject.stdout).to start_with("Usage: ") }
it { expect(subject.stderr).to start_with("Error: ") }
The file and line filter should be able to match any line of the example block, not just the first. For instance:
it "does a thing" do # Line 1
expect(true).to be_true # Line 2
end # Line 3
Running any of the following the match and run the example.
crystal spec spec/example_spec.cr:1
crystal spec spec/example_spec.cr:2
crystal spec spec/example_spec.cr:3
Implementing this involves adding a "end line" value to the Source
type in addition to updating the example filter to match the line range.
Spawned from #18
Allow positional arguments to be defined with keyword arguments. Currently, keyword arguments are treated separately from positional arguments.
class Original
def foo(arg1, arg2)
# ...
end
end
mock Original
let(fake) { mock(Original) }
specify do
expect(fake).to receive(:foo).with("arg1", arg2: "arg2")
fake.foo("arg1", "arg2")
end
The expectation fails because it recorded the call #foo("arg1", "arg2")
and not #foo("arg1", arg2: "arg2")
. The two calls are technically the same to Crystal, but Spectator treats them differently.
Just another thing that rspec has that would be wonderful to have here is the --format html
command line flag. Idk if you already have it planned, but it's not on the roadmap so I figured I'd put it here.
I'm attempting to mock/stub IO::Memory#gets
to verify that it is called multiple times until non-empty user input is given. However, Spectator seems to be looking for the original IO#gets
method? Is this due to the numeric overloads of IO#gets
?
require "./spec_helper"
require "../src/command_kit/interactive"
Spectator.describe CommandKit::Interactive do
module TestInteractive
class TestCommand
include CommandKit::Interactive
def initialize(**kwargs)
super(**kwargs)
end
end
end
let(command_class) { TestInteractive::TestCommand }
describe "#included" do
subject { command_class }
it { expect(subject).to be_a(CommandKit::Stdio) }
end
mock IO::Memory do
stub print
stub gets : String?
end
let(stdout) { IO::Memory.new }
let(stdin) { IO::Memory.new }
let(stderr) { IO::Memory.new }
subject do
command_class.new(stdout: stdout, stdin: stdin, stderr: stderr)
end
let(prompt) { "Prompt" }
describe "#ask" do
let(input) { "foo" }
it "must print a prompt, read input, and return the input" do
expect(stdout).to receive(:print).with("#{prompt}: ")
expect(stdin).to receive(:gets).and_return(input)
expect(subject.ask(prompt)).to eq(input)
end
end
end
$ crystal spec spec/interactive_spec.cr
Showing last frame. Use --error-trace for full trace.
There was a problem expanding macro 'stub'
Code in spec/interactive_spec.cr:25:1
25 | stub gets : String?
^
Called macro defined in lib/spectator/src/spectator/mocks/stubs.cr:3:13
3 | private macro stub(definition, *types, _file = __FILE__, _line = __LINE__, return_type = :undefined, &block)
Which expanded to:
> 9 | __temp_70.mocks.record_call(self, __temp_72)
> 10 | if (__temp_73 = __temp_70.mocks.find_stub(self, __temp_72))
> 11 | return __temp_73.call!(__temp_71) { previous_def }
^-----------
Error: there is no previous definition of 'gets'
Currently, the scope of method stubs with a default scope is executed within the mocked type. This should be changed to execute in the scope of the spec.
struct MyStruct
def stubbed_method
answer
end
def answer
1
end
end
Spectator.describe MyStruct do
let(answer) { 42 }
mock MyStruct do
stub stubbed_method { answer }
end
it "returns the answer" do
expect(subject.stubbed_method).to eq(42) # Fails, reports 1
end
end
Issue originally raised in #14
I believe this is a bug.
spectator/src/spectator/name_node_filter.cr
Lines 11 to 13 in 60085eb
it should be @name.includes?(node.to_s)
?
Seems as though libs can't be mocked the way you can a class or module. I don't know if it's possible given the constraints of the language, but I thought I'd put it out there.
It seems that default arguments are not working properly when the method in question is being stubbed.
require "spectator"
class Person
def initialize(@dog = Dog.new)
end
def pet
@dog.pet
end
end
class Dog
def initialize()
end
def pet(times = 3)
"woof" * times
end
end
Spectator.describe Person do
mock Dog do
stub pet(times)
end
describe "#pet" do
it "pets the persons dog" do
dog = Dog.new
person = Person.new(dog)
allow(dog).to receive(pet()).and_return("woof")
result = person.pet
expect(result).to be("woof")
expect(dog).to have_received(pet())
end
end
end
Adding another stub
to the mock (something like stub pet()
) also does not work.
I noticed that contain
automatically wraps the given argument in { }
as a Tuple. It would be useful to be able to call contain()
with multiple splatted arguments.
alias ArgsTuple = Tuple(String, Extractor::MetaType, Extractor::MetaFormat, String, Extractor::MetaData)
let(metadata_file) { File.join(fixtures_dir,"image-metadata.yml") }
let(expected_metadata) do
Array(ArgsTuple).from_yaml(File.read(metadata_file))
end
it "should extract metadata from a file" do
findings = [] of ArgsTuple
subject.extract(data) do |plugin_name,type,format,mime_type,data|
findings << {plugin_name, type, format, mime_type, data}
end
expect(findings).to contain(*expected_metadata)
end
Code in spec/extractor_spec.cr:53:27
53 | expect(findings).to contain(*expected_metadata)
^
Called macro defined in lib/spectator/src/spectator/dsl/matchers.cr:452:5
452 | macro contain(*expected)
Which expanded to:
> 1 | __temp_1618 = ::Spectator::TestValue.new({*expected_metadata}, "*expected_metadata")
^
Error: unterminated call
Hi! Thanks for your library! It makes me, an RSpec lover, feel more at home. :-)
After a while I tried to run my project specs (that used to work) and something is failing now. I don't really know what caused the failure; perhaps a newer Crystal version.
My tail of shards.lock
:
spectator:
git: https://gitlab.com/arctic-fox/spectator.git
version: 0.11.5
$ crystal --version
Crystal 1.7.2 (2023-01-23)
LLVM: 14.0.6
Default target: aarch64-apple-darwin22.1.0
$ git clone https://github.com/kaukas/crystal-cassandra
$ shards install
$ crystal spec spec/cassandra/dbapi_spec.cr --error-trace
There was a problem expanding macro 'default_stub'
Called macro defined in lib/spectator/src/spectator/mocks/stubbable.cr:108:13
108 | private macro default_stub(method)
Which expanded to:
> 1 |
> 2 |
> 3 |
> 4 |
> 5 |
> 6 | def should(
> 7 | expectation : BeAExpectation(T), failure_message : String | ::Nil = nil, *, file = __FILE__, line = __LINE__,
> 8 |
> 9 |
> 10 | ) : T forall T
> 11 | super(expectation, failure_message, file: file, line: line)
> 12 | end
> 13 |
> 14 |
> 15 |
> 16 |
> 17 | def should(
> 18 | expectation : BeAExpectation(T), failure_message : String | ::Nil = nil, *, file = __FILE__, line = __LINE__,
> 19 |
> 20 |
> 21 | ) : T forall T
> 22 |
> 23 | # Capture information about the call.
> 24 | __temp_153 = ::Spectator::MethodCall.build(
> 25 | :should,
> 26 | ::NamedTuple.new(
> 27 | "expectation": expectation, "failure_message": failure_message,
> 28 | ),
> 29 | :"", ,
> 30 | ::NamedTuple.new(
> 31 | "file": file, "line": line,
> 32 | ).merge()
> 33 | )
> 34 | _spectator_record_call(__temp_153)
> 35 |
> 36 | # Attempt to find a stub that satisfies the method call and arguments.
> 37 | # Finding a suitable stub is delegated to the type including the `Stubbable` module.
> 38 | if __temp_154 = _spectator_find_stub(__temp_153)
> 39 | # Cast the stub or return value to the expected type.
> 40 | # This is necessary to match the expected return type of the original method.
> 41 | _spectator_cast_stub_value(__temp_154, __temp_153, typeof(previous_def),
> 42 | :raise)
> 43 | else
> 44 | # Delegate missing stub behavior to concrete type.
> 45 | _spectator_stub_fallback(__temp_153, typeof(previous_def)) do
> 46 | # Use the default response for the method.
> 47 | previous_def
> 48 | end
> 49 | end
> 50 | end
> 51 |
Error: unterminated call
From what I gather splat names are expected to either be non empty strings on nils. However, this particular should
definition has an unnamed splat which ends up as an empty string when rendered in the macro (see :"", ,
on line 29).
Unfortunately, my quick attempt to reproduce it with a spec in spectator failed. Perhaps you'd have better luck?..
Thank you!
require "spectator"
def four
2 + 2
end
Spectator.describe Int32 do
let!(:x) { four }
it("returns 4") { expect(x).to eq(4) }
end
Does not compile because crystal can't infer what type four
returns. Strangely though if I replace let!
with let
it works fine and passes as it should. While I get why it doesn't compile with naive implementation of let!
, you seem to have solved it with let
, so it would be nice if it worked with let!
too.
Hi all, just a small problem in the matcher with crystal 0.33...
Given this file test_problem.cr
require "spectator"
Spectator.describe "A test" do
it "should work" do
expect(7).to be_between(1, 10)
end
end
I get this:
crystal spec ./test_problem.cr
There was a problem expanding macro 'be_between'
Code in test_problem.cr:4:18
4 | expect(7).to be_between(1, 10)
^
Called macro defined in lib/spectator/src/spectator/dsl/matchers.cr:344:5
344 | macro be_between(min, max)
Which expanded to:
> 1 | __temp_1031 = Range.new(1, 10))
^
Error: unexpected token: )
Running with --error-trace gives this trace:
In test_problem.cr:3:3
3 | it "should work" do
^
Error: expanding macro
There was a problem expanding macro 'it'
Called macro defined in lib/spectator/src/spectator/dsl/examples.cr:6:5
6 | macro it(description = nil, _source_file = __FILE__, _source_line = __LINE__, &block)
Which expanded to:
> 1 |
> 2 | def __temp_35
> 3 | (expect(7)).to(be_between(1, 10))
> 4 | end
> 5 |
> 6 |
> 7 | __temp_36 = ::Spectator::Source.new("/Users/tuad/work/slack/voronoi_stuff/crystal_voro/voronoi_geometry/test_problem.cr", 3)
> 8 | ::Spectator::SpecBuilder.add_example(
> 9 | "should work",
> 10 | __temp_36,
> 11 | SpectatorTest::Context__temp_33
> 12 | ) { |test| test.as(SpectatorTest::Context__temp_33).__temp_35 }
> 13 |
Error: instantiating 'SpectatorTest::Context__temp_33#__temp_35()'
There was a problem expanding macro 'be_between'
Called macro defined in lib/spectator/src/spectator/dsl/matchers.cr:344:5
344 | macro be_between(min, max)
Which expanded to:
> 1 | __temp_1031 = Range.new(1, 10))
> 2 | __temp_1032 = ["1", "10"].join(" to ")
> 3 | __temp_1033 = ::Spectator::TestValue.new(__temp_1031, __temp_1032)
> 4 | :Spectator::Matchers::RangeMatcher.new(__temp_1033)
> 5 |
Error: unexpected token: )
I'm trying to simply test when an "aliased" method simply calls another method. Instead of testing the return value, I thought I
would use expect(subject).to receive...
, however I receive an odd error message. Not sure whether I'm using the correct syntax?
class Test
def method2
end
def method1
method2
end
end
require "./spec_helper"
Spectator.describe Test do
describe "#method1" do
it do
expect(subject).to receive(:method2)
subject.method1
end
end
end
Failures:
1) Test#method1 received message #method2 : Nil at spec/test_spec.cr:6 At least once with any arguments
Failure: subject did not receive #method2 : Nil at spec/test_spec.cr:6 at least once with any arguments
expected: At least once with any arguments
received: 0 time(s)
When developing my Origin shard (https://github.com/pyrsmk/origin), I needed to test that the compilation is failing properly when using the shard wrongly. This is especially useful when coding a macro.
Here's what I came with in a helper:
class String
def trail(str : String) : String
return self + str if !(self =~ Regex.new("#{Regex.escape(str)}$"))
self
end
end
class SuccessfulCompileError < Exception; end
module CompileHelper
def compile_fails(path : String) : String
buffer = IO::Memory.new
result = Process.run(
"crystal",
["run", "--no-color", "--no-codegen", "spec/" + path.trail(".cr")],
error: buffer,
)
raise SuccessfulCompileError.new if result.success?
output = buffer.to_s
buffer.close
output
end
end
Here, we're defining a wire/wrong_typing.cr
with a wrong implementation of the macro. We can then test it in an example:
it "does not compile when mismatching type definition" do
expect(compile_fails("wire/wrong_typing")).to match(
/method must return Int32 but it is returning Float64/i
)
end
What are your thoughts?
Based on the example in the wiki, I cannot figure out how to use the pending
syntax, but nothing seem to compile correctly.
pending "not implemented yet" do
it "should do something" do
expect(1).to be(1)
end
end
it "should do something" do
pending "not implemented yet" do
expect(1).to be(1)
end
end
it "should do something" do
pending "not implemented yet"
expect(1).to be(1)
end
Code in spec/test_spec.cr:5:5
5 | it "should do something" do
^
Called macro defined in lib/spectator/src/spectator/dsl/examples.cr:6:5
6 | macro it(description = nil, _source_file = __FILE__, _source_line = __LINE__, &block)
Which expanded to:
> 1 |
> 2 | def __temp_1095
^
Error: can't define def inside def
The example here fails to compile - https://github.com/icy-arctic-fox/spectator/wiki/Custom-Matchers
It fails with Error: undefined constant ::Spectator::TestValue
.
Tried changing that to Spectator::Value
which seems to be the new name but that fails with:
no overload matches 'MultipleOfMatcher(ExpectedType).new' with type Spectator::Value(Int32)
Also thanks for the wonderful library you've built!
I was updating the Shrine shard and found that it had specs like it is_expected.to be_nil
. https://github.com/jetrockets/shrine.cr/blob/f897acbdc7efe4269442854bd6db1bda85fe3fdc/spec/shrine/uploaded_file_spec.cr#L73
I'm not sure if this worked on Crystal 0.35 but on 0.36 it produces an error like
In lib/spectator/src/spectator/source.cr:11:20
11 | def initialize(@file, @line)
^----
Error: instance variable '@file' of Spectator::Source must be String, not Nil
It ultimately is coming from this line returning nil instead of the filename string
spectator/src/spectator/dsl/examples.cr
Line 21 in eee5d38
After changing the specs to have the expectation wrapped in curly braces the error went away. I believe this is an invalid spec call so maybe special handling should be added to catch it and raise a friendly error?
Ran into this odd error:
256 | expect(super_set).to be_kind_of(described_class)
^
Error: unexpected token: described_class
The entire spec block:
describe "#|" do
it "should be able to be unioned with another set of chars" do
super_set = (subject | described_class['0'])
expect(super_set).to be_kind_of(described_class)
expect(super_set.includes_char?('0')).to be(true)
end
end
Clearly I'm using described_class
before the offending line, but for some reason the compiler does not like described_class
being passed/expanded within be_kind_of
.
It would be great to be able to use symbols as stub names for mocks.
But I know this a complicated edge case ^^
My specific need comes from my Origin shard (https://github.com/pyrsmk/origin) which allows the delegation of methods with complex names like []?
or <<
. When implementing it in my code, I want to test that the delegated methods are well-delegated ^^
Here's a concrete and simplified example:
# A node with delegated methods from XML::Node.
struct Node
autowire text : String, :[], :[]?
def initialize(@origin : XML::Node); end
end
Spectator.describe Node do
let(text_value) { Random::Secure.hex }
let(array_get_value) { Random.rand(Int32) }
let(array_get_or_nil_value) { [Random.rand(Int32), nil].sample }
mock XML::Node do
stub text
# This will raise a syntax error at compile-time.
# stub :[]
# stub :[]?
end
let(html) { "<!doctype html><html></html>" }
let(xml_node) { XML.parse_html(html) }
subject { described_class.new(xml_node) }
before_each do
allow(xml_node).to receive(text).and_return(text_value)
# allow(xml_node).to receive(:[]).and_return(array_get_value)
# allow(xml_node).to receive(:[]?).and_return(array_get_or_nil_value)
end
it "wires #text method" do
expect(subject.text).to eq text_value
end
# it "wires #[] method" do
# expect(subject[0]).to eq array_get_value
# end
# it "wires #[]? method" do
# expect(subject[0]?).to eq array_get_value_or_nil
# end
end
There is a strong limitation (coming from Crystal): we cannot define those methods with a return type. So the only case of such implementation would be with mocks but not doubles (or only partially).
Noticed that subject
defaults to described_class.new
. This should switch to just described_class
when described_class.is_a?(Module)
.
Showing last frame. Use --error-trace for full trace.
There was a problem expanding macro 'let'
Code in macro 'subject'
2 | let(:subject) do
^
Called macro defined in lib/spectator/src/spectator/dsl/values.cr:3:5
3 | macro let(name, &block)
Which expanded to:
> 2 |
> 3 | def subject
> 4 | described_class.new
^--
Attempting to port some RSpec specs to Crystal which tests when the top-level exit
method is called.
expect(subject).to receive(:exit).with(0)
Unfortunately, the Spectator method hook doesn't intercept the exit
method call, and the process exits prematurely.
A good example is worth 1000 words, so here is how to recreate the problem:
main.cr:
module A
def description
"test method"
end
end
main_spec.cr:
require "spectator"
require "../src/*"
Spectator.describe A do
it { is_expected.to respond_to(:description) }
end
Running crystal spec
with spectator 0.9.24 throws the following error:
Showing last frame. Use --error-trace for full trace.
In lib/spectator/src/spectator/matchers/respond_matcher.cr:22:25
22 | FailedMatchData.new(description, "#{actual.label} does not respond to #{label}", **values(snapshot))
^--
Error: no overload matches 'Spectator::Matchers::FailedMatchData.new' with types String, String, description: String
Overloads are:
- Spectator::Matchers::FailedMatchData.new(description, failure_message, values)
- Spectator::Matchers::FailedMatchData.new(description, failure_message, **values)
Based on a very brief look, I think the problem is in failed_match_data.cr#L23:
def initialize(description, @failure_message, **values)
From what I can gather, when we test a method that happens to be called description
then the key supplied as part of **values
overrides identically named method parameter. Unfortunately, I'm not familiar enough with the codebase to propose a solution or help with a PR.
module Hexdump
class Type
enum Endian
LITTLE
BIG
NETWORK = BIG
end
getter endian : Endian?
getter size : Int32
getter? signed
def initialize(@size : Int32, @signed : Bool, @endian : Endian? = nil)
end
def unsigned?
!signed?
end
end
end
require "spectator"
Spectator.describe Hexdump::Type do
describe "#initialize" do
let(size) { 4 }
let(signed) { true }
subject { described_class.new(size: size, signed: signed) }
it "must set #size" do
expect(subject.size).to eq(size)
end
it "must not set #endian" do
expect(subject.endian).to be(nil)
end
it "must set #signed?" do
expect(subject.signed?).to eq(signed)
end
context "when given endian:" do
let(endian) { Hexdump::Type::Endian::BIG }
subject do
described_class.new(size: size signed: signed, endian: endian)
end
it "must set #endian" do
expect(subject.endian).to eq(endian)
end
end
end
describe "#signed?" do
let(size) { 4 }
subject { described_class.new(size: size, signed: signed) }
context "when initialized with signed: true" do
let(signed) { true }
it do
expect(subject.signed?).to be(true)
end
end
context "when initialized with signed: false" do
let(signed) { false }
it do
expect(subject.signed?).to be(false)
end
end
end
describe "#unsigned?" do
let(size) { 4 }
subject { described_class.new(size: size, signed: signed) }
context "when initialized with signed: true" do
let(signed) { true }
it do
expect(subject.unsigned?).to be(false)
end
end
context "when initialized with signed: false" do
let(signed) { false }
it do
expect(subject.unsigned?).to be(true)
end
end
end
end
$ crystal spec spec/type_spec.cr --error-trace
error in line 1
Error: while requiring "./spec/type_spec.cr"
In spec/type_spec.cr:28:11
28 | Spectator.describe Hexdump::Type do
^-------
Error: expanding macro
In spec/type_spec.cr:28:1
28 | Spectator.describe Hexdump::Type do
^
Error: expanding macro
There was a problem expanding macro 'describe'
Called macro defined in macro 'macro_139859134662512'
46 | macro describe(description, *tags, **metadata, &block)
Which expanded to:
> 1 | class ::SpectatorTestContext
> 2 | describe(Hexdump::Type, ) do
> 3 | describe("#initialize") do
> 4 | let(size) do
> 5 | 4
> 6 | end
> 7 | let(signed) do
> 8 | true
> 9 | end
> 10 | subject do
> 11 | described_class.new(size: size, signed: signed)
> 12 | end
> 13 | it("must set #size") do
> 14 | p(size)
> 15 | (expect(subject.size)).to(eq(size))
> 16 | end
> 17 | it("must not set #endian") do
> 18 | (expect(subject.endian)).to(be(nil))
> 19 | end
> 20 | it("must set #signed?") do
> 21 | (expect(subject.signed?)).to(eq(signed))
> 22 | end
> 23 | context("when given endian:") do
> 24 | let(endian) do
> 25 | Hexdump::Type::Endian::BIG
> 26 | end
> 27 | subject do
> 28 | described_class.new(size: size(signed: signed, endian: endian))
> 29 | end
> 30 | it("must set #endian") do
> 31 | (expect(subject.endian)).to(eq(endian))
> 32 | end
> 33 | end
> 34 | end
> 35 | describe("#signed?") do
> 36 | let(size) do
> 37 | 4
> 38 | end
> 39 | subject do
> 40 | described_class.new(size: size, signed: signed)
> 41 | end
> 42 | context("when initialized with signed: true") do
> 43 | let(signed) do
> 44 | true
> 45 | end
> 46 | it do
> 47 | (expect(subject.signed?)).to(be(true))
> 48 | end
> 49 | end
> 50 | context("when initialized with signed: false") do
> 51 | let(signed) do
> 52 | false
> 53 | end
> 54 | it do
> 55 | (expect(subject.signed?)).to(be(false))
> 56 | end
> 57 | end
> 58 | end
> 59 | describe("#unsigned?") do
> 60 | let(size) do
> 61 | 4
> 62 | end
> 63 | subject do
> 64 | described_class.new(size: size, signed: signed)
> 65 | end
> 66 | context("when initialized with signed: true") do
> 67 | let(signed) do
> 68 | true
> 69 | end
> 70 | it do
> 71 | (expect(subject.unsigned?)).to(be(false))
> 72 | end
> 73 | end
> 74 | context("when initialized with signed: false") do
> 75 | let(signed) do
> 76 | false
> 77 | end
> 78 | it do
> 79 | (expect(subject.unsigned?)).to(be(true))
> 80 | end
> 81 | end
> 82 | end
> 83 | end
> 84 | end
> 85 |
Error: expanding macro
In spec/type_spec.cr:15:11
15 |
^-------
Error: expanding macro
In spec/type_spec.cr:35:1
35 | it "must set #size" do
^-
Error: expanding macro
In spec/type_spec.cr:35:1
35 | it "must set #size" do
^
Error: expanding macro
There was a problem expanding macro 'it'
Called macro defined in macro 'define_example'
17 | macro it(what = nil, *tags, **metadata, &block)
Which expanded to:
> 1 |
> 2 |
> 3 |
> 4 | _spectator_metadata(__temp_36, :metadata, )
> 5 | _spectator_metadata(__temp_61, __temp_36, )
> 6 |
> 7 |
> 8 |
> 9 |
> 10 | private def __temp_62() : Nil
> 11 | p(size)
> 12 | (expect(subject.size)).to(eq(size))
> 13 |
> 14 | end
> 15 |
> 16 | ::Spectator::DSL::Builder.add_example(
> 17 | _spectator_example_name("must set #size"),
> 18 | ::Spectator::Location.new("/home/postmodern/test/crystal/spectator/spec/type_spec.cr", 35, 39),
> 19 | -> { new.as(::Spectator::Context) },
> 20 | __temp_61
> 21 | ) do |example|
> 22 | example.with_context(SpectatorTestContext::Group__temp_51::Group__temp_55) do
> 23 |
> 24 | __temp_62
> 25 |
> 26 | end
> 27 | end
> 28 |
> 29 |
> 30 |
Error: instantiating 'Spectator::Example#with_context(SpectatorTestContext::Group__temp_51::Group__temp_55.class)'
In spec/type_spec.cr:35:1
35 | it "must set #size" do
^
Error: expanding macro
There was a problem expanding macro 'it'
Called macro defined in macro 'define_example'
17 | macro it(what = nil, *tags, **metadata, &block)
Which expanded to:
> 1 |
> 2 |
> 3 |
> 4 | _spectator_metadata(__temp_36, :metadata, )
> 5 | _spectator_metadata(__temp_61, __temp_36, )
> 6 |
> 7 |
> 8 |
> 9 |
> 10 | private def __temp_62() : Nil
> 11 | p(size)
> 12 | (expect(subject.size)).to(eq(size))
> 13 |
> 14 | end
> 15 |
> 16 | ::Spectator::DSL::Builder.add_example(
> 17 | _spectator_example_name("must set #size"),
> 18 | ::Spectator::Location.new("/home/postmodern/test/crystal/spectator/spec/type_spec.cr", 35, 39),
> 19 | -> { new.as(::Spectator::Context) },
> 20 | __temp_61
> 21 | ) do |example|
> 22 | example.with_context(SpectatorTestContext::Group__temp_51::Group__temp_55) do
> 23 |
> 24 | __temp_62
> 25 |
> 26 | end
> 27 | end
> 28 |
> 29 |
> 30 |
Error: instantiating 'Spectator::Example#with_context(SpectatorTestContext::Group__temp_51::Group__temp_55.class)'
In spec/type_spec.cr:35:1
35 | it "must set #size" do
^
Error: expanding macro
There was a problem expanding macro 'it'
Called macro defined in macro 'define_example'
17 | macro it(what = nil, *tags, **metadata, &block)
Which expanded to:
> 1 |
> 2 |
> 3 |
> 4 | _spectator_metadata(__temp_36, :metadata, )
> 5 | _spectator_metadata(__temp_61, __temp_36, )
> 6 |
> 7 |
> 8 |
> 9 |
> 10 | private def __temp_62() : Nil
> 11 | p(size)
> 12 | (expect(subject.size)).to(eq(size))
> 13 |
> 14 | end
> 15 |
> 16 | ::Spectator::DSL::Builder.add_example(
> 17 | _spectator_example_name("must set #size"),
> 18 | ::Spectator::Location.new("/home/postmodern/test/crystal/spectator/spec/type_spec.cr", 35, 39),
> 19 | -> { new.as(::Spectator::Context) },
> 20 | __temp_61
> 21 | ) do |example|
> 22 | example.with_context(SpectatorTestContext::Group__temp_51::Group__temp_55) do
> 23 |
> 24 | __temp_62
> 25 |
> 26 | end
> 27 | end
> 28 |
> 29 |
> 30 |
Error: instantiating '__temp_62()'
In spec/type_spec.cr:38:14
38 | expect(subject.size).to eq(size)
^------
Error: Undefined local variable or method 'size(signed: _named_arg0, endian: _named_arg1)'
development_dependencies:
spectator:
gitlab: arctic-fox/spectator
version: "~> 0.10"
Not sure if this is part of the intended purpose of mocks or not, but I'm attempting to mock the File
class and I get the following error:
...
> 38 | # Attempt to find a stub that satisfies the method call and arguments.
> 39 | # Finding a suitable stub is delegated to the type including the `Stubbable` module.
> 40 | if __temp_270 = _spectator_find_stub(__temp_269)
> 41 | # Cast the stub or return value to the expected type.
> 42 | # This is necessary to match the expected return type of the original method.
> 43 | _spectator_cast_stub_value(__temp_270, __temp_269, typeof(previous_def(prefix, suffix, *dir: direncoding: encodinginvalid: invalid) { |*_spectator_yargs| yield *_spectator_yargs }),
> 44 | :raise)
> 45 | else
> 46 | # Delegate missing stub behavior to concrete type.
> 47 | _spectator_stub_fallback(__temp_269, typeof(previous_def(prefix, suffix, *dir: direncoding: encodinginvalid: invalid) { |*_spectator_yargs| yield *_spectator_yargs })) do
> 48 | # Use the default response for the method.
> 49 | previous_def(prefix, suffix, *dir: direncoding: encodinginvalid: invalid) { |*_spectator_yargs| yield *_spectator_yargs }
> 50 | end
> 51 | end
> 52 | end
> 53 |
Error: unexpected token: "direncoding"
Ran into this when testing custom #===
methods.
module Test
VERSION = "0.1.0"
class Divisible
def initialize(@div : Int32)
end
def ===(other : Int32)
(other % @div) == 0
end
end
end
require "./spec_helper"
require "../src/test"
Spectator.describe Test::Divisible do
let(i) { 3 }
subject { described_class.new(i) }
describe "#===" do
it "must call === and compare the other object" do
expect(subject).to be === 6
end
end
end
$ shards install
Resolving dependencies
Fetching https://gitlab.com/arctic-fox/spectator.git
Using spectator (0.9.36)
$ crystal spec
Test::Divisible
#===
must call === and compare the other object
Failures:
1) Test::Divisible#=== must call === and compare the other object
Failure: subject does not equal 6
actual: #<Test::Divisible:0x7f2fbab6abd0 @div=3>
expected: 6
# spec/test_spec.cr:9
Finished in 341 microseconds
1 examples, 1 failures, 0 errors, 0 pending
However, if we explicitly call subject#===
within expect()
, it works:
it "must call === and compare the other object" do
expect(subject === 6).to be(true)
end
$ crystal spec
Test::Divisible
#===
must call === and compare the other object
Finished in 104 microseconds
1 examples, 0 failures, 0 errors, 0 pending
I ran into a weird issue, where macros are not being properly expanded within the description text of describe
or it
blocks.
require "./spec_helper"
Spectator.describe Test do
{% var = :foo %}
it "#{{ var.id }}" do
end
end
Test
"#{{var.id}}"
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.