mirror of
https://gitea.invidious.io/iv-org/shard-spectator.git
synced 2024-08-15 00:53:35 +00:00
Merge remote-tracking branch 'origin/master' into release/0.10
This commit is contained in:
commit
6c55301d0b
11 changed files with 140 additions and 12 deletions
20
CHANGELOG.md
20
CHANGELOG.md
|
@ -40,6 +40,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
### Removed
|
### Removed
|
||||||
- Removed one-liner `it`-syntax without braces (block).
|
- Removed one-liner `it`-syntax without braces (block).
|
||||||
|
|
||||||
|
## [0.9.40] - 2021-07-10
|
||||||
|
### Fixed
|
||||||
|
- Fix stubbing of class methods.
|
||||||
|
- Fix handling of `no_args` in some cases.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Better handling and stubbing of `Process.exit`.
|
||||||
|
|
||||||
|
## [0.9.39] - 2021-07-02
|
||||||
|
### Fixed
|
||||||
|
- Fix `expect().to receive()` syntax not implicitly stubbing the method.
|
||||||
|
- Avoid calling `NoReturn` methods from stubs. [#29](https://github.com/icy-arctic-fox/spectator/issues/29)
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Added support for `with(no_args)` for method stubs. [#28](https://github.com/icy-arctic-fox/spectator/issues/28)
|
||||||
|
- Allow creation of doubles without definition block. [#30](https://github.com/icy-arctic-fox/spectator/issues/30)
|
||||||
|
|
||||||
## [0.9.38] - 2021-05-27
|
## [0.9.38] - 2021-05-27
|
||||||
### Fixed
|
### Fixed
|
||||||
- Fix `Channel::ClosedError` when using default Crystal Logger. [#27](https://github.com/icy-arctic-fox/spectator/issues/27)
|
- Fix `Channel::ClosedError` when using default Crystal Logger. [#27](https://github.com/icy-arctic-fox/spectator/issues/27)
|
||||||
|
@ -301,7 +318,8 @@ This has been changed so that it compiles and raises an error at runtime with a
|
||||||
First version ready for public use.
|
First version ready for public use.
|
||||||
|
|
||||||
|
|
||||||
[Unreleased]: https://gitlab.com/arctic-fox/spectator/-/compare/v0.9.39...release%2F0.10
|
[Unreleased]: https://gitlab.com/arctic-fox/spectator/-/compare/v0.9.40...release%2F0.10
|
||||||
|
[0.9.40]: https://gitlab.com/arctic-fox/spectator/-/compare/v0.9.39...v0.9.40
|
||||||
[0.9.39]: https://gitlab.com/arctic-fox/spectator/-/compare/v0.9.38...v0.9.39
|
[0.9.39]: https://gitlab.com/arctic-fox/spectator/-/compare/v0.9.38...v0.9.39
|
||||||
[0.9.38]: https://gitlab.com/arctic-fox/spectator/-/compare/v0.9.37...v0.9.38
|
[0.9.38]: https://gitlab.com/arctic-fox/spectator/-/compare/v0.9.37...v0.9.38
|
||||||
[0.9.37]: https://gitlab.com/arctic-fox/spectator/-/compare/v0.9.36...v0.9.37
|
[0.9.37]: https://gitlab.com/arctic-fox/spectator/-/compare/v0.9.36...v0.9.37
|
||||||
|
|
|
@ -17,4 +17,24 @@ Spectator.describe "GitHub Issue #29" do
|
||||||
subject.goodbye
|
subject.goodbye
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "class method" do
|
||||||
|
class Foo
|
||||||
|
def self.test
|
||||||
|
exit 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
mock Foo do
|
||||||
|
stub self.exit(code)
|
||||||
|
end
|
||||||
|
|
||||||
|
subject { Foo }
|
||||||
|
|
||||||
|
it "must capture exit" do
|
||||||
|
expect(subject).to receive(:exit).with(0)
|
||||||
|
|
||||||
|
subject.test
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
37
spec/issues/github_issue_32_spec.cr
Normal file
37
spec/issues/github_issue_32_spec.cr
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
require "../spec_helper"
|
||||||
|
|
||||||
|
Spectator.describe "GitHub Issue #32" 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 self.new
|
||||||
|
stub test
|
||||||
|
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)
|
||||||
|
expect(test_class.new).to be(test_instance)
|
||||||
|
|
||||||
|
test_class.test
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
26
spec/issues/github_issue_33_spec.cr
Normal file
26
spec/issues/github_issue_33_spec.cr
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
require "../spec_helper"
|
||||||
|
|
||||||
|
Spectator.describe "GitHub Issue #33" do
|
||||||
|
class Test
|
||||||
|
def method2
|
||||||
|
end
|
||||||
|
|
||||||
|
def method1
|
||||||
|
method2
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
mock Test do
|
||||||
|
stub method2
|
||||||
|
end
|
||||||
|
|
||||||
|
describe Test do
|
||||||
|
describe "#method1" do
|
||||||
|
it do
|
||||||
|
expect(subject).to receive(:method2)
|
||||||
|
|
||||||
|
subject.method1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -98,6 +98,7 @@ module Spectator
|
||||||
elapsed = Time.measure do
|
elapsed = Time.measure do
|
||||||
error = catch { yield }
|
error = catch { yield }
|
||||||
end
|
end
|
||||||
|
error = nil if error.is_a?(SystemExit) && mocks.exit_handled?
|
||||||
{elapsed, error}
|
{elapsed, error}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
require "./mocks/*"
|
require "./mocks/*"
|
||||||
|
require "./system_exit"
|
||||||
|
|
||||||
module Spectator
|
module Spectator
|
||||||
# Functionality for mocking existing types.
|
# Functionality for mocking existing types.
|
||||||
|
@ -11,3 +12,13 @@ module Spectator
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Add default stub to `exit` method.
|
||||||
|
# This captures *most* (technically not all) attempts to exit the process.
|
||||||
|
# This stub only takes effect in example code.
|
||||||
|
# It intercepts `exit` calls and raises `Spectator::SystemExit` to prevent killing the test.
|
||||||
|
class ::Process
|
||||||
|
include ::Spectator::Mocks::Stubs
|
||||||
|
|
||||||
|
stub self.exit(code) { raise ::Spectator::SystemExit.new }
|
||||||
|
end
|
||||||
|
|
|
@ -20,6 +20,8 @@ module Spectator::Mocks
|
||||||
value = call(args) { |*ya| yield *ya }
|
value = call(args) { |*ya| yield *ya }
|
||||||
if value.is_a?(RT)
|
if value.is_a?(RT)
|
||||||
value.as(RT)
|
value.as(RT)
|
||||||
|
elsif value.nil? && RT == NoReturn
|
||||||
|
raise SystemExit.new
|
||||||
else
|
else
|
||||||
raise TypeCastError.new("The return type of stub #{self} doesn't match the expected type #{RT}")
|
raise TypeCastError.new("The return type of stub #{self} doesn't match the expected type #{RT}")
|
||||||
end
|
end
|
||||||
|
|
|
@ -41,6 +41,10 @@ module Spectator::Mocks
|
||||||
NilMethodStub.new(@name, @location, args)
|
NilMethodStub.new(@name, @location, args)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def with(args : Arguments)
|
||||||
|
NilMethodStub.new(@name, @location, @args)
|
||||||
|
end
|
||||||
|
|
||||||
def and_call_original
|
def and_call_original
|
||||||
OriginalMethodStub.new(@name, @location, @args)
|
OriginalMethodStub.new(@name, @location, @args)
|
||||||
end
|
end
|
||||||
|
|
|
@ -35,6 +35,10 @@ module Spectator::Mocks
|
||||||
fetch_type(object.class).stubs.find(&.callable?(call))
|
fetch_type(object.class).stubs.find(&.callable?(call))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def find_stub(type : T.class, call : MethodCall) forall T
|
||||||
|
fetch_type(type).stubs.find(&.callable?(call))
|
||||||
|
end
|
||||||
|
|
||||||
def record_call(object, call : MethodCall) : Nil
|
def record_call(object, call : MethodCall) : Nil
|
||||||
fetch_instance(object).calls << call
|
fetch_instance(object).calls << call
|
||||||
fetch_type(object.class).calls << call
|
fetch_type(object.class).calls << call
|
||||||
|
@ -53,6 +57,14 @@ module Spectator::Mocks
|
||||||
fetch_type(object.class).expected.any?(&.callable?(call))
|
fetch_type(object.class).expected.any?(&.callable?(call))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def exit_handled? : Bool
|
||||||
|
# Lazily check if an `exit` method was called and it was expected.
|
||||||
|
# This is okay since an `expect().to receive(:exit)` should check the details of the call.
|
||||||
|
(@entries.any? { |_key, entry| entry.expected.any? { |stub| stub.name == :exit } } ||
|
||||||
|
@all_instances.any? { |_key, entry| entry.expected.any? { |stub| stub.name == :exit } }) &&
|
||||||
|
@entries.any? { |_key, entry| entry.calls.any? { |call| call.name == :exit } }
|
||||||
|
end
|
||||||
|
|
||||||
def expect(object, stub : MethodStub) : Nil
|
def expect(object, stub : MethodStub) : Nil
|
||||||
entry = fetch_instance(object)
|
entry = fetch_instance(object)
|
||||||
entry.expected.add(stub)
|
entry.expected.add(stub)
|
||||||
|
|
|
@ -62,7 +62,7 @@ module Spectator::Mocks
|
||||||
elsif t.has_method?(name)
|
elsif t.has_method?(name)
|
||||||
:previous_def
|
:previous_def
|
||||||
else
|
else
|
||||||
"::#{name}"
|
name
|
||||||
end.id
|
end.id
|
||||||
%}
|
%}
|
||||||
|
|
||||||
|
@ -82,12 +82,8 @@ module Spectator::Mocks
|
||||||
%call = ::Spectator::Mocks::MethodCall.new({{name.symbolize}}, %args)
|
%call = ::Spectator::Mocks::MethodCall.new({{name.symbolize}}, %args)
|
||||||
%harness.mocks.record_call(self, %call)
|
%harness.mocks.record_call(self, %call)
|
||||||
if (%stub = %harness.mocks.find_stub(self, %call))
|
if (%stub = %harness.mocks.find_stub(self, %call))
|
||||||
if typeof({{original}}) == NoReturn
|
|
||||||
return %stub.call!(%args) { nil }
|
|
||||||
else
|
|
||||||
return %stub.call!(%args) { {{original}} }
|
return %stub.call!(%args) { {{original}} }
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
{% if body && !body.is_a?(Nop) || return_type.is_a?(ArrayLiteral) %}
|
{% if body && !body.is_a?(Nop) || return_type.is_a?(ArrayLiteral) %}
|
||||||
%method({{args.splat}})
|
%method({{args.splat}})
|
||||||
|
@ -105,12 +101,8 @@ module Spectator::Mocks
|
||||||
%call = ::Spectator::Mocks::MethodCall.new({{name.symbolize}}, %args)
|
%call = ::Spectator::Mocks::MethodCall.new({{name.symbolize}}, %args)
|
||||||
%harness.mocks.record_call(self, %call)
|
%harness.mocks.record_call(self, %call)
|
||||||
if (%stub = %harness.mocks.find_stub(self, %call))
|
if (%stub = %harness.mocks.find_stub(self, %call))
|
||||||
if typeof({{original}}) == NoReturn
|
|
||||||
return %stub.call!(%args) { nil }
|
|
||||||
else
|
|
||||||
return %stub.call!(%args) { {{original}} { |*%ya| yield *%ya } }
|
return %stub.call!(%args) { {{original}} { |*%ya| yield *%ya } }
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
{% if body && !body.is_a?(Nop) || return_type.is_a?(ArrayLiteral) %}
|
{% if body && !body.is_a?(Nop) || return_type.is_a?(ArrayLiteral) %}
|
||||||
%method({{args.splat}}) { |*%ya| yield *%ya }
|
%method({{args.splat}}) { |*%ya| yield *%ya }
|
||||||
|
|
5
src/spectator/system_exit.cr
Normal file
5
src/spectator/system_exit.cr
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
module Spectator
|
||||||
|
# Exception raised when `exit` is called and intercepted from a stub.
|
||||||
|
class SystemExit < Exception
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue