smartest 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -17
- data/DEVELOPMENT.md +44 -28
- data/README.md +33 -27
- data/SMARTEST_DESIGN.md +33 -33
- data/lib/smartest/fixture.rb +4 -4
- data/lib/smartest/fixture_set.rb +7 -7
- data/lib/smartest/init_browser_generator.rb +3 -3
- data/lib/smartest/reporter.rb +12 -12
- data/lib/smartest/runner.rb +14 -14
- data/lib/smartest/test_result.rb +11 -11
- data/lib/smartest/version.rb +1 -1
- data/smartest/simple_stub_test.rb +2 -2
- data/smartest/smartest_test.rb +41 -22
- data/smartest.gemspec +10 -3
- metadata +12 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e192221b36307c61da97572a41e866b137f1a668bb9db26bda75fbd6489df89a
|
|
4
|
+
data.tar.gz: eb3ee1b6ffae15eaf27b88ffbe4d01bfedbb763a7a1d52bb99bae34bd7e8dc97
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 916737afbb7af9447c571d16de7c08e73ea52686462a2b99c4378a118e38d613fdbe3152f13fbf089fd518b8930c3e8f927451678e58e23777150a3d98158608
|
|
7
|
+
data.tar.gz: fe618761cbe53ad37bde38182018835b7566d7a205d0edd92c7e43547d002b131771bce65f2f82a66b7dc3afa770c190d3d8ccfd4e374398e0e2341aa22baf98
|
data/CHANGELOG.md
CHANGED
|
@@ -1,19 +1,24 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
-
|
|
8
|
-
|
|
9
|
-
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
-
|
|
17
|
-
|
|
18
|
-
- Add
|
|
19
|
-
|
|
3
|
+
## 0.5.0
|
|
4
|
+
|
|
5
|
+
### Breaking Changes
|
|
6
|
+
|
|
7
|
+
- Rename the fixture teardown registration API from `cleanup` to
|
|
8
|
+
`on_teardown`. The old `cleanup` method is no longer available.
|
|
9
|
+
- Rename teardown failure output from cleanup terminology to teardown
|
|
10
|
+
terminology.
|
|
11
|
+
|
|
12
|
+
## 0.4.0
|
|
13
|
+
|
|
14
|
+
### New Features
|
|
15
|
+
|
|
16
|
+
- Add fixture-scoped method stub helpers: `simple_stub_any_instance_of` and
|
|
17
|
+
`simple_stub`.
|
|
18
|
+
- Add `with_stub_const` for block-scoped constant stubbing in test bodies,
|
|
19
|
+
`around_test`, and `around_suite`.
|
|
20
|
+
- Support Ruby 2.7 and newer.
|
|
21
|
+
|
|
22
|
+
## 0.1.0 - 0.3.2
|
|
23
|
+
|
|
24
|
+
- Initial release.
|
data/DEVELOPMENT.md
CHANGED
|
@@ -13,7 +13,7 @@ Smartest is a Ruby test runner focused on:
|
|
|
13
13
|
- top-level test definitions
|
|
14
14
|
- class-based fixtures
|
|
15
15
|
- explicit keyword-argument fixture dependencies
|
|
16
|
-
- optional
|
|
16
|
+
- optional teardown for fixtures that need teardown
|
|
17
17
|
- suite-scoped fixtures for expensive shared resources
|
|
18
18
|
- a small internal architecture that is easy to reason about
|
|
19
19
|
|
|
@@ -224,7 +224,7 @@ Responsibilities:
|
|
|
224
224
|
- class-level `suite_fixture` DSL for suite-scoped fixtures
|
|
225
225
|
- stores fixture definitions
|
|
226
226
|
- supports inheritance
|
|
227
|
-
- exposes `
|
|
227
|
+
- exposes `on_teardown` to fixture blocks
|
|
228
228
|
- optionally delegates helper methods to `ExecutionContext`
|
|
229
229
|
- does not delegate `skip` or `pending` to fixture blocks
|
|
230
230
|
|
|
@@ -293,8 +293,8 @@ Responsibilities:
|
|
|
293
293
|
- find fixture definitions
|
|
294
294
|
- resolve fixture dependencies
|
|
295
295
|
- cache fixture values for its scope
|
|
296
|
-
- collect
|
|
297
|
-
- run
|
|
296
|
+
- collect teardown blocks
|
|
297
|
+
- run teardown blocks in reverse order
|
|
298
298
|
- detect duplicate fixture names
|
|
299
299
|
- detect circular dependencies
|
|
300
300
|
|
|
@@ -332,9 +332,9 @@ Responsibilities:
|
|
|
332
332
|
- create a fresh `FixtureSet` per test
|
|
333
333
|
- resolve test keyword fixtures
|
|
334
334
|
- run test body
|
|
335
|
-
- run
|
|
335
|
+
- run teardown in `ensure`
|
|
336
336
|
- track skipped and pending test state
|
|
337
|
-
- run suite fixture
|
|
337
|
+
- run suite fixture teardown after all tests
|
|
338
338
|
- produce `TestResult`
|
|
339
339
|
- notify reporter
|
|
340
340
|
|
|
@@ -373,7 +373,7 @@ rescue Exception => error
|
|
|
373
373
|
TestResult.failed(test_case, error)
|
|
374
374
|
end
|
|
375
375
|
ensure
|
|
376
|
-
fixture_set&.
|
|
376
|
+
fixture_set&.run_teardowns
|
|
377
377
|
end
|
|
378
378
|
```
|
|
379
379
|
|
|
@@ -424,7 +424,7 @@ end
|
|
|
424
424
|
|
|
425
425
|
fixture :server do
|
|
426
426
|
server = TestServer.start
|
|
427
|
-
|
|
427
|
+
on_teardown { server.stop }
|
|
428
428
|
server
|
|
429
429
|
end
|
|
430
430
|
|
|
@@ -456,12 +456,12 @@ resolve :logged_in_client
|
|
|
456
456
|
evaluate logged_in_client
|
|
457
457
|
cache logged_in_client
|
|
458
458
|
run test body
|
|
459
|
-
run
|
|
459
|
+
run teardown stack
|
|
460
460
|
```
|
|
461
461
|
|
|
462
|
-
##
|
|
462
|
+
## Teardown behavior
|
|
463
463
|
|
|
464
|
-
Fixture
|
|
464
|
+
Fixture teardown is optional.
|
|
465
465
|
|
|
466
466
|
Fixture without teardown:
|
|
467
467
|
|
|
@@ -476,33 +476,33 @@ Fixture with teardown:
|
|
|
476
476
|
```ruby
|
|
477
477
|
fixture :server do
|
|
478
478
|
server = TestServer.start
|
|
479
|
-
|
|
479
|
+
on_teardown { server.stop }
|
|
480
480
|
|
|
481
481
|
server.wait_until_ready!
|
|
482
482
|
server
|
|
483
483
|
end
|
|
484
484
|
```
|
|
485
485
|
|
|
486
|
-
`
|
|
487
|
-
|
|
486
|
+
`on_teardown` should register a block on the current fixture set. Regular fixture
|
|
487
|
+
teardown blocks run after the test. `suite_fixture` teardown blocks run after all tests.
|
|
488
488
|
|
|
489
|
-
|
|
489
|
+
Teardown blocks must run:
|
|
490
490
|
|
|
491
491
|
- after the test body
|
|
492
492
|
- after the suite for suite-scoped fixtures
|
|
493
493
|
- after failed tests
|
|
494
|
-
- after fixture setup errors, if
|
|
494
|
+
- after fixture setup errors, if teardown was already registered
|
|
495
495
|
- in reverse registration order
|
|
496
496
|
|
|
497
497
|
Implementation:
|
|
498
498
|
|
|
499
499
|
```ruby
|
|
500
|
-
def
|
|
501
|
-
@
|
|
500
|
+
def add_teardown(&block)
|
|
501
|
+
@teardowns << block
|
|
502
502
|
end
|
|
503
503
|
|
|
504
|
-
def
|
|
505
|
-
@
|
|
504
|
+
def run_teardowns
|
|
505
|
+
@teardowns.reverse_each(&:call)
|
|
506
506
|
end
|
|
507
507
|
```
|
|
508
508
|
|
|
@@ -621,17 +621,17 @@ A practical approach:
|
|
|
621
621
|
- recursive fixture resolution
|
|
622
622
|
- per-test caching
|
|
623
623
|
|
|
624
|
-
### Phase 4:
|
|
624
|
+
### Phase 4: Teardown
|
|
625
625
|
|
|
626
|
-
- `
|
|
627
|
-
-
|
|
628
|
-
-
|
|
626
|
+
- `on_teardown { ... }`
|
|
627
|
+
- teardown stack on `FixtureSet`
|
|
628
|
+
- teardown in `ensure`
|
|
629
629
|
|
|
630
630
|
### Phase 5: Suite-scoped fixtures
|
|
631
631
|
|
|
632
632
|
- `suite_fixture :name do ... end`
|
|
633
633
|
- suite-level fixture cache
|
|
634
|
-
- suite
|
|
634
|
+
- suite teardown after all tests
|
|
635
635
|
- test fixtures may depend on suite fixtures
|
|
636
636
|
- suite fixtures may not depend on test fixtures
|
|
637
637
|
|
|
@@ -662,14 +662,14 @@ A practical approach:
|
|
|
662
662
|
|
|
663
663
|
- `around_suite do |suite| ... end`
|
|
664
664
|
- run hooks around the full suite body
|
|
665
|
-
- include suite fixture
|
|
665
|
+
- include suite fixture teardown inside the wrapped body
|
|
666
666
|
- report hook failures as suite failures
|
|
667
667
|
|
|
668
668
|
### Phase 8: Test hooks
|
|
669
669
|
|
|
670
670
|
- `around_test do |test| ... end`
|
|
671
671
|
- snapshot file-local hooks when each test is registered
|
|
672
|
-
- run hooks around fixture setup, test body, and fixture
|
|
672
|
+
- run hooks around fixture setup, test body, and fixture teardown
|
|
673
673
|
- expose `use_fixture` and `use_matcher` only inside hook contexts
|
|
674
674
|
- make `around_test` registered from `around_suite` suite-wide
|
|
675
675
|
|
|
@@ -720,7 +720,7 @@ end
|
|
|
720
720
|
```
|
|
721
721
|
|
|
722
722
|
```ruby
|
|
723
|
-
|
|
723
|
+
on_teardown { ... }
|
|
724
724
|
```
|
|
725
725
|
|
|
726
726
|
```ruby
|
|
@@ -843,6 +843,22 @@ Before releasing:
|
|
|
843
843
|
- run a sample project against the installed gem
|
|
844
844
|
- push the release tag
|
|
845
845
|
|
|
846
|
+
### Changelog updates
|
|
847
|
+
|
|
848
|
+
Update `CHANGELOG.md` from the actual diff between released versions, not from
|
|
849
|
+
commit messages. For an already tagged release, compare the previous release tag
|
|
850
|
+
to the release tag and inspect the code/docs changes, for example:
|
|
851
|
+
|
|
852
|
+
```bash
|
|
853
|
+
git diff --stat 0.4.0..0.5.0
|
|
854
|
+
git diff 0.4.0..0.5.0
|
|
855
|
+
```
|
|
856
|
+
|
|
857
|
+
Before the new tag exists, compare the latest release tag to the release branch
|
|
858
|
+
or working tree and inspect the same kind of diff. Only document breaking
|
|
859
|
+
changes and notable new features. Do not copy every commit message or list
|
|
860
|
+
documentation-only maintenance changes unless they materially affect users.
|
|
861
|
+
|
|
846
862
|
Example commands:
|
|
847
863
|
|
|
848
864
|
```bash
|
data/README.md
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
# Smartest
|
|
1
|
+
# Smartest
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
[](https://rubygems.org/gems/smartest)
|
|
4
|
+
|
|
5
|
+
Smartest is a Ruby test runner that brings pytest-style fixtures to Ruby,
|
|
6
|
+
with explicit fixture dependencies, automatic teardown, and Playwright-friendly
|
|
7
|
+
browser testing.
|
|
5
8
|
|
|
6
9
|
Tests request fixtures with Ruby keyword arguments. Fixtures define their own
|
|
7
10
|
dependencies the same way:
|
|
@@ -10,7 +13,7 @@ dependencies the same way:
|
|
|
10
13
|
class WebFixture < Smartest::Fixture
|
|
11
14
|
fixture :server do
|
|
12
15
|
server = TestServer.start
|
|
13
|
-
|
|
16
|
+
on_teardown { server.stop }
|
|
14
17
|
server
|
|
15
18
|
end
|
|
16
19
|
|
|
@@ -45,7 +48,7 @@ idea: tests should explicitly declare their dependencies.
|
|
|
45
48
|
RSpec `let` is useful when examples need lazy helper methods. Minitest `setup`
|
|
46
49
|
is simple for xUnit-style setup. Rails fixtures and FactoryBot are great for
|
|
47
50
|
test data. Smartest is aimed at tests where setup resources, dependency graphs,
|
|
48
|
-
and
|
|
51
|
+
and teardown should be visible in the test signature and fixture definitions.
|
|
49
52
|
|
|
50
53
|
```ruby
|
|
51
54
|
test("GET /me") do |logged_in_client:|
|
|
@@ -67,6 +70,9 @@ end
|
|
|
67
70
|
|
|
68
71
|
Smartest requires Ruby 2.7 or newer.
|
|
69
72
|
|
|
73
|
+
Smartest is published as the `smartest` RubyGem:
|
|
74
|
+
[Smartest on RubyGems](https://rubygems.org/gems/smartest).
|
|
75
|
+
|
|
70
76
|
Add this line to your application's Gemfile:
|
|
71
77
|
|
|
72
78
|
```ruby
|
|
@@ -477,7 +483,7 @@ released after the full suite finishes:
|
|
|
477
483
|
class BrowserFixture < Smartest::Fixture
|
|
478
484
|
suite_fixture :browser do
|
|
479
485
|
browser = Browser.launch
|
|
480
|
-
|
|
486
|
+
on_teardown { browser.close }
|
|
481
487
|
browser
|
|
482
488
|
end
|
|
483
489
|
|
|
@@ -488,7 +494,7 @@ end
|
|
|
488
494
|
```
|
|
489
495
|
|
|
490
496
|
Suite fixtures are lazy: setup runs the first time a test requests the fixture,
|
|
491
|
-
and
|
|
497
|
+
and teardown runs once after all tests finish. Test-scoped fixtures can depend on
|
|
492
498
|
suite fixtures, but suite fixtures cannot depend on test-scoped fixtures.
|
|
493
499
|
|
|
494
500
|
## Suite hooks
|
|
@@ -504,8 +510,8 @@ end
|
|
|
504
510
|
```
|
|
505
511
|
|
|
506
512
|
The hook receives a run target and must call `suite.run` exactly once. The block
|
|
507
|
-
wraps every test, test-scoped fixture setup and
|
|
508
|
-
suite fixture
|
|
513
|
+
wraps every test, test-scoped fixture setup and teardown, suite fixture setup, and
|
|
514
|
+
suite fixture teardown.
|
|
509
515
|
|
|
510
516
|
Fixture and matcher registrations made before `suite.run` are applied to that
|
|
511
517
|
run:
|
|
@@ -546,7 +552,7 @@ end
|
|
|
546
552
|
```
|
|
547
553
|
|
|
548
554
|
The hook receives a run target and must call `test.run` exactly once. It wraps
|
|
549
|
-
fixture setup, the test body, and fixture
|
|
555
|
+
fixture setup, the test body, and fixture teardown.
|
|
550
556
|
|
|
551
557
|
`around_test` is file-scoped when it is written directly in a test file. Smartest
|
|
552
558
|
copies the current file's `around_test` hooks when each `test` is registered, so
|
|
@@ -580,20 +586,20 @@ end
|
|
|
580
586
|
|
|
581
587
|
Fixture classes registered from `around_test` must define only test-scoped
|
|
582
588
|
fixtures. If a class defines `suite_fixture`, register it from `around_suite`
|
|
583
|
-
instead so its cache and
|
|
589
|
+
instead so its cache and teardown belong to the suite lifecycle.
|
|
584
590
|
|
|
585
591
|
`use_fixture` and `use_matcher` are only available inside `around_suite` or
|
|
586
592
|
`around_test` blocks. They are not top-level DSL methods.
|
|
587
593
|
|
|
588
594
|
## Fixtures with teardown
|
|
589
595
|
|
|
590
|
-
Not every fixture needs teardown. For fixtures that do, use `
|
|
596
|
+
Not every fixture needs teardown. For fixtures that do, use `on_teardown`.
|
|
591
597
|
|
|
592
598
|
```ruby
|
|
593
599
|
class WebFixture < Smartest::Fixture
|
|
594
600
|
fixture :server do
|
|
595
601
|
server = TestServer.start
|
|
596
|
-
|
|
602
|
+
on_teardown { server.stop }
|
|
597
603
|
|
|
598
604
|
server.wait_until_ready!
|
|
599
605
|
server
|
|
@@ -605,8 +611,8 @@ class WebFixture < Smartest::Fixture
|
|
|
605
611
|
end
|
|
606
612
|
```
|
|
607
613
|
|
|
608
|
-
`
|
|
609
|
-
that means after the test. For `suite_fixture`,
|
|
614
|
+
`on_teardown` blocks run after the fixture's scope finishes. For regular fixtures
|
|
615
|
+
that means after the test. For `suite_fixture`, teardown runs after the full
|
|
610
616
|
suite.
|
|
611
617
|
|
|
612
618
|
They are executed in reverse order of registration.
|
|
@@ -614,7 +620,7 @@ They are executed in reverse order of registration.
|
|
|
614
620
|
```ruby
|
|
615
621
|
fixture :temp_dir do
|
|
616
622
|
dir = Dir.mktmpdir
|
|
617
|
-
|
|
623
|
+
on_teardown { FileUtils.rm_rf(dir) }
|
|
618
624
|
|
|
619
625
|
dir
|
|
620
626
|
end
|
|
@@ -625,19 +631,19 @@ Recommended pattern:
|
|
|
625
631
|
```ruby
|
|
626
632
|
fixture :server do
|
|
627
633
|
server = TestServer.start
|
|
628
|
-
|
|
634
|
+
on_teardown { server.stop }
|
|
629
635
|
|
|
630
636
|
server.wait_until_ready!
|
|
631
637
|
server
|
|
632
638
|
end
|
|
633
639
|
```
|
|
634
640
|
|
|
635
|
-
Register
|
|
641
|
+
Register teardown immediately after acquiring the resource, before later setup steps that may fail.
|
|
636
642
|
|
|
637
643
|
## Stubs
|
|
638
644
|
|
|
639
645
|
Use simple stub helpers when a fixture needs to temporarily replace a Ruby
|
|
640
|
-
method and reset it during
|
|
646
|
+
method and reset it during teardown:
|
|
641
647
|
|
|
642
648
|
```ruby
|
|
643
649
|
class PaymentFixture < Smartest::Fixture
|
|
@@ -678,7 +684,7 @@ constants in test bodies, `around_test`, or `around_suite`. Constant stubs are
|
|
|
678
684
|
process-global; avoid concurrent tests that stub the same constant.
|
|
679
685
|
|
|
680
686
|
The method stub helpers call `Smartest::SimpleStub` internally, apply the stub,
|
|
681
|
-
register `
|
|
687
|
+
register `on_teardown { stub.reset }`, and return the stub object.
|
|
682
688
|
`with_stub_const` records the previous constant value, replaces it, yields to
|
|
683
689
|
the block, and restores or removes the constant with `ensure`.
|
|
684
690
|
|
|
@@ -695,7 +701,7 @@ the current Fiber, and `reset!` raises
|
|
|
695
701
|
class WebFixture < Smartest::Fixture
|
|
696
702
|
fixture :server do
|
|
697
703
|
server = TestServer.start
|
|
698
|
-
|
|
704
|
+
on_teardown { server.stop }
|
|
699
705
|
|
|
700
706
|
server.wait_until_ready!
|
|
701
707
|
server
|
|
@@ -755,7 +761,7 @@ client setup
|
|
|
755
761
|
user setup
|
|
756
762
|
logged_in_client setup
|
|
757
763
|
test body
|
|
758
|
-
server
|
|
764
|
+
server teardown
|
|
759
765
|
```
|
|
760
766
|
|
|
761
767
|
## Registering fixture classes
|
|
@@ -784,15 +790,15 @@ Fixture names must be unique across registered fixture classes.
|
|
|
784
790
|
|
|
785
791
|
If two fixture classes define the same fixture name, Smartest raises an error.
|
|
786
792
|
|
|
787
|
-
## Suite hooks and fixture
|
|
793
|
+
## Suite hooks and fixture teardown
|
|
788
794
|
|
|
789
|
-
Suite hooks are separate from fixture
|
|
795
|
+
Suite hooks are separate from fixture teardown. Use fixture teardown for
|
|
790
796
|
resource-specific teardown:
|
|
791
797
|
|
|
792
798
|
```ruby
|
|
793
799
|
fixture :server do
|
|
794
800
|
server = TestServer.start
|
|
795
|
-
|
|
801
|
+
on_teardown { server.stop }
|
|
796
802
|
server
|
|
797
803
|
end
|
|
798
804
|
```
|
|
@@ -849,7 +855,7 @@ Example:
|
|
|
849
855
|
class WebFixture < Smartest::Fixture
|
|
850
856
|
fixture :server do
|
|
851
857
|
server = TestServer.start
|
|
852
|
-
|
|
858
|
+
on_teardown { server.stop }
|
|
853
859
|
server
|
|
854
860
|
end
|
|
855
861
|
|
|
@@ -912,7 +918,7 @@ Smartest currently focuses on a small runner API:
|
|
|
912
918
|
- class-based fixtures
|
|
913
919
|
- keyword-argument fixture injection
|
|
914
920
|
- fixture dependencies through keyword arguments
|
|
915
|
-
- fixture
|
|
921
|
+
- fixture teardown
|
|
916
922
|
- suite-scoped fixtures through `suite_fixture`
|
|
917
923
|
- fixture-scoped method stubs and block-scoped constant stubs
|
|
918
924
|
- suite hooks with `around_suite`
|
data/SMARTEST_DESIGN.md
CHANGED
|
@@ -30,7 +30,7 @@ Fixture definitions:
|
|
|
30
30
|
class WebFixture < Smartest::Fixture
|
|
31
31
|
fixture :server do
|
|
32
32
|
server = TestServer.start
|
|
33
|
-
|
|
33
|
+
on_teardown { server.stop }
|
|
34
34
|
|
|
35
35
|
server.wait_until_ready!
|
|
36
36
|
server
|
|
@@ -195,12 +195,12 @@ server teardown
|
|
|
195
195
|
|
|
196
196
|
This is especially complex when fixtures depend on other fixtures.
|
|
197
197
|
|
|
198
|
-
Smartest instead chooses `
|
|
198
|
+
Smartest instead chooses `on_teardown` for the MVP:
|
|
199
199
|
|
|
200
200
|
```ruby
|
|
201
201
|
fixture :server do
|
|
202
202
|
server = TestServer.start
|
|
203
|
-
|
|
203
|
+
on_teardown { server.stop }
|
|
204
204
|
|
|
205
205
|
server.wait_until_ready!
|
|
206
206
|
server
|
|
@@ -214,7 +214,7 @@ This has several advantages:
|
|
|
214
214
|
- teardown is local to the fixture that owns the resource
|
|
215
215
|
- implementation is simple
|
|
216
216
|
- fixture dependencies remain ordinary recursive resolution
|
|
217
|
-
-
|
|
217
|
+
- teardown runs in `ensure`
|
|
218
218
|
|
|
219
219
|
Not every fixture needs teardown, so teardown should not shape the entire fixture API.
|
|
220
220
|
|
|
@@ -236,12 +236,12 @@ fixture :article do |user:|
|
|
|
236
236
|
end
|
|
237
237
|
```
|
|
238
238
|
|
|
239
|
-
A fixture may register
|
|
239
|
+
A fixture may register teardown.
|
|
240
240
|
|
|
241
241
|
```ruby
|
|
242
242
|
fixture :temp_dir do
|
|
243
243
|
dir = Dir.mktmpdir
|
|
244
|
-
|
|
244
|
+
on_teardown { FileUtils.rm_rf(dir) }
|
|
245
245
|
dir
|
|
246
246
|
end
|
|
247
247
|
```
|
|
@@ -316,7 +316,7 @@ Runner
|
|
|
316
316
|
├── creates FixtureSet
|
|
317
317
|
├── resolves keyword fixtures
|
|
318
318
|
├── executes test body
|
|
319
|
-
├── runs
|
|
319
|
+
├── runs teardown
|
|
320
320
|
└── reports TestResult
|
|
321
321
|
```
|
|
322
322
|
|
|
@@ -343,7 +343,7 @@ end
|
|
|
343
343
|
|
|
344
344
|
fixture :server do
|
|
345
345
|
server = TestServer.start
|
|
346
|
-
|
|
346
|
+
on_teardown { server.stop }
|
|
347
347
|
server
|
|
348
348
|
end
|
|
349
349
|
|
|
@@ -363,7 +363,7 @@ resolve logged_in_client
|
|
|
363
363
|
requires server
|
|
364
364
|
resolve server
|
|
365
365
|
evaluate server block
|
|
366
|
-
register
|
|
366
|
+
register teardown
|
|
367
367
|
cache server
|
|
368
368
|
evaluate client block with server:
|
|
369
369
|
cache client
|
|
@@ -376,7 +376,7 @@ resolve logged_in_client
|
|
|
376
376
|
|
|
377
377
|
execute test body with logged_in_client:
|
|
378
378
|
|
|
379
|
-
run
|
|
379
|
+
run teardown stack in reverse order
|
|
380
380
|
```
|
|
381
381
|
|
|
382
382
|
## Fixture caching
|
|
@@ -397,33 +397,33 @@ shared for the runner lifetime.
|
|
|
397
397
|
This keeps regular fixtures isolated while allowing explicit suite fixtures for
|
|
398
398
|
expensive shared resources.
|
|
399
399
|
|
|
400
|
-
##
|
|
400
|
+
## Teardown stack
|
|
401
401
|
|
|
402
|
-
`FixtureSet` owns a
|
|
402
|
+
`FixtureSet` owns a teardown stack for one fixture scope.
|
|
403
403
|
|
|
404
404
|
```ruby
|
|
405
|
-
@
|
|
405
|
+
@teardowns = []
|
|
406
406
|
```
|
|
407
407
|
|
|
408
408
|
Fixture blocks can call:
|
|
409
409
|
|
|
410
410
|
```ruby
|
|
411
|
-
|
|
411
|
+
on_teardown { resource.close }
|
|
412
412
|
```
|
|
413
413
|
|
|
414
414
|
This delegates to:
|
|
415
415
|
|
|
416
416
|
```ruby
|
|
417
|
-
fixture_set.
|
|
417
|
+
fixture_set.add_teardown(&block)
|
|
418
418
|
```
|
|
419
419
|
|
|
420
|
-
For test-scoped fixtures,
|
|
420
|
+
For test-scoped fixtures, teardown runs after the test in reverse order:
|
|
421
421
|
|
|
422
422
|
```ruby
|
|
423
|
-
@
|
|
423
|
+
@teardowns.reverse_each(&:call)
|
|
424
424
|
```
|
|
425
425
|
|
|
426
|
-
For suite-scoped fixtures,
|
|
426
|
+
For suite-scoped fixtures, teardown runs after all tests. Reverse order matters
|
|
427
427
|
because later resources may depend on earlier ones.
|
|
428
428
|
|
|
429
429
|
Example:
|
|
@@ -431,18 +431,18 @@ Example:
|
|
|
431
431
|
```ruby
|
|
432
432
|
fixture :server do
|
|
433
433
|
server = TestServer.start
|
|
434
|
-
|
|
434
|
+
on_teardown { server.stop }
|
|
435
435
|
server
|
|
436
436
|
end
|
|
437
437
|
|
|
438
438
|
fixture :browser do |server:|
|
|
439
439
|
browser = Browser.launch(server.url)
|
|
440
|
-
|
|
440
|
+
on_teardown { browser.close }
|
|
441
441
|
browser
|
|
442
442
|
end
|
|
443
443
|
```
|
|
444
444
|
|
|
445
|
-
|
|
445
|
+
Teardown should run:
|
|
446
446
|
|
|
447
447
|
```text
|
|
448
448
|
browser.close
|
|
@@ -669,7 +669,7 @@ Fixture block execution happens on the fixture instance:
|
|
|
669
669
|
fixture_instance.instance_exec(**dependencies, &definition.block)
|
|
670
670
|
```
|
|
671
671
|
|
|
672
|
-
This allows fixture helper methods and `
|
|
672
|
+
This allows fixture helper methods and `on_teardown` to be private instance methods.
|
|
673
673
|
|
|
674
674
|
## Helper methods in fixtures
|
|
675
675
|
|
|
@@ -964,7 +964,7 @@ end
|
|
|
964
964
|
```
|
|
965
965
|
|
|
966
966
|
`around_suite` wraps the full suite body, including all tests and suite fixture
|
|
967
|
-
|
|
967
|
+
teardown. The hook receives a run target and must call `suite.run` exactly once.
|
|
968
968
|
Multiple hooks compose in registration order, with the first hook as the
|
|
969
969
|
outermost wrapper.
|
|
970
970
|
|
|
@@ -1013,7 +1013,7 @@ For `around_test`, those registrations are test-run local and must happen before
|
|
|
1013
1013
|
`test.run`.
|
|
1014
1014
|
|
|
1015
1015
|
Fixture classes registered from `around_test` must not define `suite_fixture`.
|
|
1016
|
-
Suite-scoped fixtures need suite-level cache and
|
|
1016
|
+
Suite-scoped fixtures need suite-level cache and teardown ownership, so classes
|
|
1017
1017
|
with suite-scoped fixtures must be registered from `around_suite`.
|
|
1018
1018
|
|
|
1019
1019
|
Potential simpler per-test API:
|
|
@@ -1036,7 +1036,7 @@ Order:
|
|
|
1036
1036
|
before hooks
|
|
1037
1037
|
fixture setup
|
|
1038
1038
|
test body
|
|
1039
|
-
fixture
|
|
1039
|
+
fixture teardown
|
|
1040
1040
|
after hooks
|
|
1041
1041
|
```
|
|
1042
1042
|
|
|
@@ -1047,12 +1047,12 @@ fixture setup
|
|
|
1047
1047
|
before hooks
|
|
1048
1048
|
test body
|
|
1049
1049
|
after hooks
|
|
1050
|
-
fixture
|
|
1050
|
+
fixture teardown
|
|
1051
1051
|
```
|
|
1052
1052
|
|
|
1053
1053
|
This needs a final decision later.
|
|
1054
1054
|
|
|
1055
|
-
Fixture
|
|
1055
|
+
Fixture teardown already handles resource-specific teardown.
|
|
1056
1056
|
|
|
1057
1057
|
### Around-test parallelism note
|
|
1058
1058
|
|
|
@@ -1086,7 +1086,7 @@ Expensive shared resources can use `suite_fixture`:
|
|
|
1086
1086
|
```ruby
|
|
1087
1087
|
suite_fixture :server do
|
|
1088
1088
|
server = TestServer.start
|
|
1089
|
-
|
|
1089
|
+
on_teardown { server.stop }
|
|
1090
1090
|
server
|
|
1091
1091
|
end
|
|
1092
1092
|
```
|
|
@@ -1099,7 +1099,7 @@ Supported scopes:
|
|
|
1099
1099
|
`fixture :name do ... end` creates a test-scoped fixture.
|
|
1100
1100
|
|
|
1101
1101
|
`suite_fixture :name do ... end` creates a suite-scoped fixture. It is lazy:
|
|
1102
|
-
setup runs the first time a test requests it, and
|
|
1102
|
+
setup runs the first time a test requests it, and teardown runs after all tests.
|
|
1103
1103
|
|
|
1104
1104
|
Test-scoped fixtures may depend on suite-scoped fixtures. Suite-scoped fixtures
|
|
1105
1105
|
may depend only on other suite-scoped fixtures.
|
|
@@ -1136,7 +1136,7 @@ Benefits:
|
|
|
1136
1136
|
- reusable fixture modules
|
|
1137
1137
|
- clearer organization
|
|
1138
1138
|
- fewer global definitions
|
|
1139
|
-
- natural place for
|
|
1139
|
+
- natural place for teardown helper
|
|
1140
1140
|
|
|
1141
1141
|
Example:
|
|
1142
1142
|
|
|
@@ -1197,7 +1197,7 @@ Pros:
|
|
|
1197
1197
|
- easy dependency extraction
|
|
1198
1198
|
- easy duplicate detection
|
|
1199
1199
|
- easy source locations
|
|
1200
|
-
- easy
|
|
1200
|
+
- easy teardown integration
|
|
1201
1201
|
|
|
1202
1202
|
### `fixture def user`
|
|
1203
1203
|
|
|
@@ -1241,7 +1241,7 @@ Reason:
|
|
|
1241
1241
|
|
|
1242
1242
|
- requires around-chain execution
|
|
1243
1243
|
- complicates dependency handling
|
|
1244
|
-
- not needed if `
|
|
1244
|
+
- not needed if `on_teardown` exists
|
|
1245
1245
|
- makes fixture API more complex
|
|
1246
1246
|
|
|
1247
1247
|
Could be added later as advanced API.
|
|
@@ -1271,7 +1271,7 @@ class AppFixture < Smartest::Fixture
|
|
|
1271
1271
|
|
|
1272
1272
|
fixture :server do
|
|
1273
1273
|
server = TestServer.start
|
|
1274
|
-
|
|
1274
|
+
on_teardown { server.stop }
|
|
1275
1275
|
server
|
|
1276
1276
|
end
|
|
1277
1277
|
|
data/lib/smartest/fixture.rb
CHANGED
|
@@ -59,10 +59,10 @@ module Smartest
|
|
|
59
59
|
|
|
60
60
|
private
|
|
61
61
|
|
|
62
|
-
def
|
|
63
|
-
raise ArgumentError, "
|
|
62
|
+
def on_teardown(&block)
|
|
63
|
+
raise ArgumentError, "on_teardown block is required" unless block
|
|
64
64
|
|
|
65
|
-
@fixture_set.
|
|
65
|
+
@fixture_set.add_teardown(&block)
|
|
66
66
|
end
|
|
67
67
|
|
|
68
68
|
def simple_stub_any_instance_of(klass, method_name, &block)
|
|
@@ -75,7 +75,7 @@ module Smartest
|
|
|
75
75
|
|
|
76
76
|
def apply_simple_stub(stub)
|
|
77
77
|
stub.apply!
|
|
78
|
-
|
|
78
|
+
on_teardown { stub.reset }
|
|
79
79
|
stub
|
|
80
80
|
end
|
|
81
81
|
|
data/lib/smartest/fixture_set.rb
CHANGED
|
@@ -9,7 +9,7 @@ module Smartest
|
|
|
9
9
|
@parent = parent
|
|
10
10
|
@cache = {}
|
|
11
11
|
@setup_errors = {}
|
|
12
|
-
@
|
|
12
|
+
@teardowns = []
|
|
13
13
|
@resolving = []
|
|
14
14
|
|
|
15
15
|
build_fixture_index
|
|
@@ -49,17 +49,17 @@ module Smartest
|
|
|
49
49
|
@resolving.pop if @resolving.last == symbol_name
|
|
50
50
|
end
|
|
51
51
|
|
|
52
|
-
def
|
|
53
|
-
raise ArgumentError, "
|
|
52
|
+
def add_teardown(&block)
|
|
53
|
+
raise ArgumentError, "on_teardown block is required" unless block
|
|
54
54
|
|
|
55
|
-
@
|
|
55
|
+
@teardowns << block
|
|
56
56
|
end
|
|
57
57
|
|
|
58
|
-
def
|
|
58
|
+
def run_teardowns
|
|
59
59
|
errors = []
|
|
60
60
|
|
|
61
|
-
@
|
|
62
|
-
|
|
61
|
+
@teardowns.reverse_each do |teardown|
|
|
62
|
+
teardown.call
|
|
63
63
|
rescue Exception => error
|
|
64
64
|
raise if Smartest.fatal_exception?(error)
|
|
65
65
|
|
|
@@ -14,7 +14,7 @@ module Smartest
|
|
|
14
14
|
runtime = Playwright.create(
|
|
15
15
|
playwright_cli_executable_path: "./node_modules/.bin/playwright",
|
|
16
16
|
)
|
|
17
|
-
|
|
17
|
+
on_teardown { runtime.stop }
|
|
18
18
|
runtime.playwright
|
|
19
19
|
end
|
|
20
20
|
|
|
@@ -35,13 +35,13 @@ module Smartest
|
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
browser = playwright.send(browser_type).launch(**launch_options)
|
|
38
|
-
|
|
38
|
+
on_teardown { browser.close }
|
|
39
39
|
browser
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
fixture :page do |browser:|
|
|
43
43
|
context = browser.new_context
|
|
44
|
-
|
|
44
|
+
on_teardown { context.close }
|
|
45
45
|
context.new_page
|
|
46
46
|
end
|
|
47
47
|
end
|
data/lib/smartest/reporter.rb
CHANGED
|
@@ -21,14 +21,14 @@ module Smartest
|
|
|
21
21
|
@io.puts record_line(result)
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
-
def finish(results,
|
|
24
|
+
def finish(results, suite_teardown_errors: [], suite_errors: [])
|
|
25
25
|
failures = results.select(&:failed?)
|
|
26
26
|
skipped = results.select(&:skipped?)
|
|
27
27
|
pending = results.select(&:pending?)
|
|
28
28
|
|
|
29
29
|
report_failures(failures) if failures.any?
|
|
30
30
|
report_suite_errors(suite_errors) if suite_errors.any?
|
|
31
|
-
|
|
31
|
+
report_suite_teardown_errors(suite_teardown_errors) if suite_teardown_errors.any?
|
|
32
32
|
report_profile(results) if @profile_count && @profile_count.positive?
|
|
33
33
|
|
|
34
34
|
@io.puts
|
|
@@ -39,9 +39,9 @@ module Smartest
|
|
|
39
39
|
suite_label = suite_errors.count == 1 ? "suite failure" : "suite failures"
|
|
40
40
|
summary = "#{summary}, #{suite_errors.count} #{suite_label}"
|
|
41
41
|
end
|
|
42
|
-
if
|
|
43
|
-
|
|
44
|
-
summary = "#{summary}, #{
|
|
42
|
+
if suite_teardown_errors.any?
|
|
43
|
+
teardown_label = suite_teardown_errors.count == 1 ? "suite teardown" : "suite teardowns"
|
|
44
|
+
summary = "#{summary}, #{suite_teardown_errors.count} #{teardown_label} failed"
|
|
45
45
|
end
|
|
46
46
|
@io.puts summary
|
|
47
47
|
end
|
|
@@ -72,7 +72,7 @@ module Smartest
|
|
|
72
72
|
@io.puts "#{index + 1}) #{result.test_case.name}"
|
|
73
73
|
report_location(result.test_case.location)
|
|
74
74
|
report_error(result.error) if result.error
|
|
75
|
-
result.
|
|
75
|
+
result.teardown_errors.each { |error| report_teardown_error(error) }
|
|
76
76
|
@io.puts
|
|
77
77
|
end
|
|
78
78
|
end
|
|
@@ -89,14 +89,14 @@ module Smartest
|
|
|
89
89
|
end
|
|
90
90
|
end
|
|
91
91
|
|
|
92
|
-
def
|
|
92
|
+
def report_suite_teardown_errors(errors)
|
|
93
93
|
@io.puts
|
|
94
|
-
@io.puts "Suite
|
|
94
|
+
@io.puts "Suite teardown failures:"
|
|
95
95
|
@io.puts
|
|
96
96
|
|
|
97
97
|
errors.each_with_index do |error, index|
|
|
98
|
-
@io.puts "#{index + 1}) suite
|
|
99
|
-
|
|
98
|
+
@io.puts "#{index + 1}) suite teardown"
|
|
99
|
+
report_teardown_error(error)
|
|
100
100
|
@io.puts
|
|
101
101
|
end
|
|
102
102
|
end
|
|
@@ -148,8 +148,8 @@ module Smartest
|
|
|
148
148
|
report_backtrace(error)
|
|
149
149
|
end
|
|
150
150
|
|
|
151
|
-
def
|
|
152
|
-
@io.puts "
|
|
151
|
+
def report_teardown_error(error)
|
|
152
|
+
@io.puts " teardown failed: #{error.class}: #{error.message}"
|
|
153
153
|
report_backtrace(error)
|
|
154
154
|
end
|
|
155
155
|
|
data/lib/smartest/runner.rb
CHANGED
|
@@ -10,7 +10,7 @@ module Smartest
|
|
|
10
10
|
|
|
11
11
|
def run
|
|
12
12
|
results = []
|
|
13
|
-
|
|
13
|
+
suite_teardown_errors = []
|
|
14
14
|
suite_errors = []
|
|
15
15
|
@suite_fixture_set = nil
|
|
16
16
|
|
|
@@ -18,7 +18,7 @@ module Smartest
|
|
|
18
18
|
|
|
19
19
|
begin
|
|
20
20
|
run_around_suite_hooks(@suite.around_suite_hooks.dup) do
|
|
21
|
-
run_tests(results,
|
|
21
|
+
run_tests(results, suite_teardown_errors)
|
|
22
22
|
end
|
|
23
23
|
rescue Exception => error
|
|
24
24
|
raise if Smartest.fatal_exception?(error)
|
|
@@ -28,16 +28,16 @@ module Smartest
|
|
|
28
28
|
|
|
29
29
|
@reporter.finish(
|
|
30
30
|
results,
|
|
31
|
-
|
|
31
|
+
suite_teardown_errors: suite_teardown_errors,
|
|
32
32
|
suite_errors: suite_errors
|
|
33
33
|
)
|
|
34
34
|
|
|
35
|
-
results.any?(&:failed?) ||
|
|
35
|
+
results.any?(&:failed?) || suite_teardown_errors.any? || suite_errors.any? ? 1 : 0
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
private
|
|
39
39
|
|
|
40
|
-
def run_tests(results,
|
|
40
|
+
def run_tests(results, suite_teardown_errors)
|
|
41
41
|
begin
|
|
42
42
|
@tests.each do |test_case|
|
|
43
43
|
result = run_one(test_case)
|
|
@@ -45,7 +45,7 @@ module Smartest
|
|
|
45
45
|
@reporter.record(result)
|
|
46
46
|
end
|
|
47
47
|
ensure
|
|
48
|
-
|
|
48
|
+
suite_teardown_errors.concat(@suite_fixture_set.run_teardowns) if @suite_fixture_set
|
|
49
49
|
@suite_fixture_set = nil
|
|
50
50
|
end
|
|
51
51
|
end
|
|
@@ -68,13 +68,13 @@ module Smartest
|
|
|
68
68
|
started_at = now
|
|
69
69
|
error = nil
|
|
70
70
|
skipped = nil
|
|
71
|
-
|
|
71
|
+
teardown_errors = []
|
|
72
72
|
run_state = TestRunState.new
|
|
73
73
|
test_run = TestRun.new(
|
|
74
74
|
fixture_classes: @suite.fixture_classes,
|
|
75
75
|
matcher_modules: @suite.matcher_modules
|
|
76
76
|
) do |fixture_classes:, matcher_modules:, helper_modules:|
|
|
77
|
-
run_test_body(test_case, fixture_classes, matcher_modules, helper_modules, run_state,
|
|
77
|
+
run_test_body(test_case, fixture_classes, matcher_modules, helper_modules, run_state, teardown_errors)
|
|
78
78
|
end
|
|
79
79
|
|
|
80
80
|
begin
|
|
@@ -89,12 +89,12 @@ module Smartest
|
|
|
89
89
|
|
|
90
90
|
duration = now - started_at
|
|
91
91
|
|
|
92
|
-
return TestResult.failed(test_case: test_case, error: nil, duration: duration,
|
|
92
|
+
return TestResult.failed(test_case: test_case, error: nil, duration: duration, teardown_errors: teardown_errors) if skipped && teardown_errors.any?
|
|
93
93
|
return TestResult.skipped(test_case: test_case, reason: skipped.reason, duration: duration) if skipped
|
|
94
94
|
|
|
95
95
|
if run_state.pending?
|
|
96
96
|
if error && !around_test_protocol_error?(error)
|
|
97
|
-
return TestResult.failed(test_case: test_case, error: nil, duration: duration,
|
|
97
|
+
return TestResult.failed(test_case: test_case, error: nil, duration: duration, teardown_errors: teardown_errors) if teardown_errors.any?
|
|
98
98
|
|
|
99
99
|
return TestResult.pending(test_case: test_case, reason: run_state.pending_reason, duration: duration)
|
|
100
100
|
end
|
|
@@ -102,19 +102,19 @@ module Smartest
|
|
|
102
102
|
error ||= PendingPassedError.new(run_state.pending_reason)
|
|
103
103
|
end
|
|
104
104
|
|
|
105
|
-
if error ||
|
|
105
|
+
if error || teardown_errors.any?
|
|
106
106
|
TestResult.failed(
|
|
107
107
|
test_case: test_case,
|
|
108
108
|
error: error,
|
|
109
109
|
duration: duration,
|
|
110
|
-
|
|
110
|
+
teardown_errors: teardown_errors
|
|
111
111
|
)
|
|
112
112
|
else
|
|
113
113
|
TestResult.passed(test_case: test_case, duration: duration)
|
|
114
114
|
end
|
|
115
115
|
end
|
|
116
116
|
|
|
117
|
-
def run_test_body(test_case, fixture_classes, matcher_modules, helper_modules, run_state,
|
|
117
|
+
def run_test_body(test_case, fixture_classes, matcher_modules, helper_modules, run_state, teardown_errors)
|
|
118
118
|
context = build_context(matcher_modules, run_state, helper_modules)
|
|
119
119
|
fixture_set = nil
|
|
120
120
|
|
|
@@ -123,7 +123,7 @@ module Smartest
|
|
|
123
123
|
fixtures = fixture_set.resolve_keywords(test_case.fixture_names)
|
|
124
124
|
context.instance_exec(**fixtures, &test_case.block)
|
|
125
125
|
ensure
|
|
126
|
-
|
|
126
|
+
teardown_errors.concat(fixture_set.run_teardowns) if fixture_set
|
|
127
127
|
end
|
|
128
128
|
end
|
|
129
129
|
|
data/lib/smartest/test_result.rb
CHANGED
|
@@ -2,59 +2,59 @@
|
|
|
2
2
|
|
|
3
3
|
module Smartest
|
|
4
4
|
class TestResult
|
|
5
|
-
attr_reader :test_case, :status, :error, :duration, :
|
|
5
|
+
attr_reader :test_case, :status, :error, :duration, :teardown_errors, :reason
|
|
6
6
|
|
|
7
|
-
def self.passed(test_case:, duration:,
|
|
7
|
+
def self.passed(test_case:, duration:, teardown_errors: [])
|
|
8
8
|
new(
|
|
9
9
|
test_case: test_case,
|
|
10
10
|
status: :passed,
|
|
11
11
|
error: nil,
|
|
12
12
|
reason: nil,
|
|
13
13
|
duration: duration,
|
|
14
|
-
|
|
14
|
+
teardown_errors: teardown_errors
|
|
15
15
|
)
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
def self.failed(test_case:, error:, duration:,
|
|
18
|
+
def self.failed(test_case:, error:, duration:, teardown_errors: [])
|
|
19
19
|
new(
|
|
20
20
|
test_case: test_case,
|
|
21
21
|
status: :failed,
|
|
22
22
|
error: error,
|
|
23
23
|
reason: nil,
|
|
24
24
|
duration: duration,
|
|
25
|
-
|
|
25
|
+
teardown_errors: teardown_errors
|
|
26
26
|
)
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
def self.skipped(test_case:, reason:, duration:,
|
|
29
|
+
def self.skipped(test_case:, reason:, duration:, teardown_errors: [])
|
|
30
30
|
new(
|
|
31
31
|
test_case: test_case,
|
|
32
32
|
status: :skipped,
|
|
33
33
|
error: nil,
|
|
34
34
|
reason: reason,
|
|
35
35
|
duration: duration,
|
|
36
|
-
|
|
36
|
+
teardown_errors: teardown_errors
|
|
37
37
|
)
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
-
def self.pending(test_case:, reason:, duration:,
|
|
40
|
+
def self.pending(test_case:, reason:, duration:, teardown_errors: [])
|
|
41
41
|
new(
|
|
42
42
|
test_case: test_case,
|
|
43
43
|
status: :pending,
|
|
44
44
|
error: nil,
|
|
45
45
|
reason: reason,
|
|
46
46
|
duration: duration,
|
|
47
|
-
|
|
47
|
+
teardown_errors: teardown_errors
|
|
48
48
|
)
|
|
49
49
|
end
|
|
50
50
|
|
|
51
|
-
def initialize(test_case:, status:, error:, reason:, duration:,
|
|
51
|
+
def initialize(test_case:, status:, error:, reason:, duration:, teardown_errors:)
|
|
52
52
|
@test_case = test_case
|
|
53
53
|
@status = status
|
|
54
54
|
@error = error
|
|
55
55
|
@reason = reason
|
|
56
56
|
@duration = duration
|
|
57
|
-
@
|
|
57
|
+
@teardown_errors = teardown_errors
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
def passed?
|
data/lib/smartest/version.rb
CHANGED
|
@@ -96,7 +96,7 @@ test("simple stub can be reset from a fresh stub object") do
|
|
|
96
96
|
expect(SimpleStubSelfTestSubject.new("Alice").greeting("Hi")).to eq("Hi, Alice")
|
|
97
97
|
end
|
|
98
98
|
|
|
99
|
-
test("simple_stub_any_instance_of applies and resets from fixture
|
|
99
|
+
test("simple_stub_any_instance_of applies and resets from fixture teardown") do
|
|
100
100
|
fixture_class = Class.new(Smartest::Fixture) do
|
|
101
101
|
fixture :stubbed_name do
|
|
102
102
|
simple_stub_any_instance_of(SimpleStubSelfTestSubject, :name) { "fixture #{@name}" }
|
|
@@ -127,7 +127,7 @@ test("simple_stub_any_instance_of applies and resets from fixture cleanup") do
|
|
|
127
127
|
expect(status).to eq(0)
|
|
128
128
|
end
|
|
129
129
|
|
|
130
|
-
test("simple_stub applies and resets singleton methods from fixture
|
|
130
|
+
test("simple_stub applies and resets singleton methods from fixture teardown") do
|
|
131
131
|
fixture_class = Class.new(Smartest::Fixture) do
|
|
132
132
|
fixture :stubbed_time do
|
|
133
133
|
simple_stub(SimpleStubSelfTestClock, :now) { :stubbed_now }
|
data/smartest/smartest_test.rb
CHANGED
|
@@ -697,7 +697,7 @@ test("caches fixture values within one test") do
|
|
|
697
697
|
expect(calls).to eq(1)
|
|
698
698
|
end
|
|
699
699
|
|
|
700
|
-
test("suite fixtures are created once and
|
|
700
|
+
test("suite fixtures are created once and torn down after the suite") do
|
|
701
701
|
events = []
|
|
702
702
|
servers = []
|
|
703
703
|
|
|
@@ -705,7 +705,7 @@ test("suite fixtures are created once and cleaned up after the suite") do
|
|
|
705
705
|
suite_fixture :server do
|
|
706
706
|
events << :server_setup
|
|
707
707
|
server = Object.new
|
|
708
|
-
|
|
708
|
+
on_teardown { events << :server_teardown }
|
|
709
709
|
server
|
|
710
710
|
end
|
|
711
711
|
end
|
|
@@ -718,7 +718,7 @@ test("suite fixtures are created once and cleaned up after the suite") do
|
|
|
718
718
|
status, = SmartestSelfTest.run_suite(suite)
|
|
719
719
|
|
|
720
720
|
expect(status).to eq(0)
|
|
721
|
-
expect(events).to eq(%i[server_setup first second
|
|
721
|
+
expect(events).to eq(%i[server_setup first second server_teardown])
|
|
722
722
|
expect(servers.length).to eq(2)
|
|
723
723
|
expect(servers[0].object_id).to eq(servers[1].object_id)
|
|
724
724
|
end
|
|
@@ -773,14 +773,14 @@ test("suite fixtures cannot depend on test fixtures") do
|
|
|
773
773
|
expect(output).to include("suite-scoped fixture server cannot depend on test-scoped fixture user")
|
|
774
774
|
end
|
|
775
775
|
|
|
776
|
-
test("suite fixture setup failures are cached and
|
|
776
|
+
test("suite fixture setup failures are cached and torn down once") do
|
|
777
777
|
calls = 0
|
|
778
778
|
events = []
|
|
779
779
|
|
|
780
780
|
fixture_class = Class.new(Smartest::Fixture) do
|
|
781
781
|
suite_fixture :server do
|
|
782
782
|
calls += 1
|
|
783
|
-
|
|
783
|
+
on_teardown { events << :server_teardown }
|
|
784
784
|
raise "server setup failed"
|
|
785
785
|
end
|
|
786
786
|
end
|
|
@@ -794,17 +794,17 @@ test("suite fixture setup failures are cached and cleaned up once") do
|
|
|
794
794
|
|
|
795
795
|
expect(status).to eq(1)
|
|
796
796
|
expect(calls).to eq(1)
|
|
797
|
-
expect(events).to eq([:
|
|
797
|
+
expect(events).to eq([:server_teardown])
|
|
798
798
|
expect(output.scan("RuntimeError: server setup failed").length).to eq(2)
|
|
799
799
|
end
|
|
800
800
|
|
|
801
|
-
test("around_suite wraps tests and suite fixture
|
|
801
|
+
test("around_suite wraps tests and suite fixture teardown") do
|
|
802
802
|
events = []
|
|
803
803
|
|
|
804
804
|
fixture_class = Class.new(Smartest::Fixture) do
|
|
805
805
|
suite_fixture :server do
|
|
806
806
|
events << :server_setup
|
|
807
|
-
|
|
807
|
+
on_teardown { events << :server_teardown }
|
|
808
808
|
:server
|
|
809
809
|
end
|
|
810
810
|
end
|
|
@@ -821,7 +821,7 @@ test("around_suite wraps tests and suite fixture cleanup") do
|
|
|
821
821
|
status, = SmartestSelfTest.run_suite(suite)
|
|
822
822
|
|
|
823
823
|
expect(status).to eq(0)
|
|
824
|
-
expect(events).to eq(%i[around_before server_setup test
|
|
824
|
+
expect(events).to eq(%i[around_before server_setup test server_teardown around_after])
|
|
825
825
|
end
|
|
826
826
|
|
|
827
827
|
test("around_suite hooks run in registration order") do
|
|
@@ -886,13 +886,13 @@ test("around_suite can register suite-wide around_test hooks") do
|
|
|
886
886
|
expect(events).to eq(%i[around_test_before test around_test_after])
|
|
887
887
|
end
|
|
888
888
|
|
|
889
|
-
test("around_test wraps fixture setup, test body, and
|
|
889
|
+
test("around_test wraps fixture setup, test body, and teardown") do
|
|
890
890
|
events = []
|
|
891
891
|
|
|
892
892
|
fixture_class = Class.new(Smartest::Fixture) do
|
|
893
893
|
fixture :resource do
|
|
894
894
|
events << :fixture_setup
|
|
895
|
-
|
|
895
|
+
on_teardown { events << :fixture_teardown }
|
|
896
896
|
:resource
|
|
897
897
|
end
|
|
898
898
|
end
|
|
@@ -918,7 +918,7 @@ test("around_test wraps fixture setup, test body, and cleanup") do
|
|
|
918
918
|
status, = SmartestSelfTest.run_suite(suite)
|
|
919
919
|
|
|
920
920
|
expect(status).to eq(0)
|
|
921
|
-
expect(events).to eq(%i[around_test_before fixture_setup test
|
|
921
|
+
expect(events).to eq(%i[around_test_before fixture_setup test fixture_teardown around_test_after])
|
|
922
922
|
end
|
|
923
923
|
|
|
924
924
|
test("around_test can register fixtures for one test run") do
|
|
@@ -1232,10 +1232,10 @@ test("around_suite must call suite.run") do
|
|
|
1232
1232
|
expect(output).to include("Smartest::AroundSuiteRunError: around_suite hook did not call suite.run")
|
|
1233
1233
|
end
|
|
1234
1234
|
|
|
1235
|
-
test("suite
|
|
1235
|
+
test("suite teardown failures fail the run") do
|
|
1236
1236
|
fixture_class = Class.new(Smartest::Fixture) do
|
|
1237
1237
|
suite_fixture :browser do
|
|
1238
|
-
|
|
1238
|
+
on_teardown { raise "browser close failed" }
|
|
1239
1239
|
:browser
|
|
1240
1240
|
end
|
|
1241
1241
|
end
|
|
@@ -1247,22 +1247,22 @@ test("suite cleanup failures fail the run") do
|
|
|
1247
1247
|
status, output = SmartestSelfTest.run_suite(suite)
|
|
1248
1248
|
|
|
1249
1249
|
expect(status).to eq(1)
|
|
1250
|
-
expect(output).to include("Suite
|
|
1251
|
-
expect(output).to include("
|
|
1252
|
-
expect(output).to include("1 test, 1 passed, 0 failed, 1 suite
|
|
1250
|
+
expect(output).to include("Suite teardown failures:")
|
|
1251
|
+
expect(output).to include("teardown failed: RuntimeError: browser close failed")
|
|
1252
|
+
expect(output).to include("1 test, 1 passed, 0 failed, 1 suite teardown failed")
|
|
1253
1253
|
end
|
|
1254
1254
|
|
|
1255
|
-
test("runs
|
|
1255
|
+
test("runs teardown in reverse order after failures") do
|
|
1256
1256
|
events = []
|
|
1257
1257
|
|
|
1258
1258
|
fixture_class = Class.new(Smartest::Fixture) do
|
|
1259
1259
|
fixture :server do
|
|
1260
|
-
|
|
1260
|
+
on_teardown { events << :server }
|
|
1261
1261
|
:server
|
|
1262
1262
|
end
|
|
1263
1263
|
|
|
1264
1264
|
fixture :browser do |server:|
|
|
1265
|
-
|
|
1265
|
+
on_teardown { events << :browser }
|
|
1266
1266
|
server
|
|
1267
1267
|
end
|
|
1268
1268
|
end
|
|
@@ -1277,12 +1277,12 @@ test("runs cleanup in reverse order after failures") do
|
|
|
1277
1277
|
expect(events).to eq(%i[browser server])
|
|
1278
1278
|
end
|
|
1279
1279
|
|
|
1280
|
-
test("runs
|
|
1280
|
+
test("runs teardown when fixture setup fails after teardown registration") do
|
|
1281
1281
|
events = []
|
|
1282
1282
|
|
|
1283
1283
|
fixture_class = Class.new(Smartest::Fixture) do
|
|
1284
1284
|
fixture :server do
|
|
1285
|
-
|
|
1285
|
+
on_teardown { events << :server }
|
|
1286
1286
|
raise "server setup failed"
|
|
1287
1287
|
end
|
|
1288
1288
|
end
|
|
@@ -1298,6 +1298,25 @@ test("runs cleanup when fixture setup fails after cleanup registration") do
|
|
|
1298
1298
|
expect(output).to include("RuntimeError: server setup failed")
|
|
1299
1299
|
end
|
|
1300
1300
|
|
|
1301
|
+
test("does not keep cleanup as a fixture teardown alias") do
|
|
1302
|
+
fixture_class = Class.new(Smartest::Fixture) do
|
|
1303
|
+
fixture :server do
|
|
1304
|
+
cleanup { :server }
|
|
1305
|
+
:server
|
|
1306
|
+
end
|
|
1307
|
+
end
|
|
1308
|
+
|
|
1309
|
+
suite = Smartest::Suite.new
|
|
1310
|
+
suite.fixture_classes.add(fixture_class)
|
|
1311
|
+
suite.tests.add(SmartestSelfTest.test_case("needs server", proc { |server:| expect(server).to eq(:server) }))
|
|
1312
|
+
|
|
1313
|
+
status, output = SmartestSelfTest.run_suite(suite)
|
|
1314
|
+
|
|
1315
|
+
expect(status).to eq(1)
|
|
1316
|
+
expect(output).to include("NoMethodError")
|
|
1317
|
+
expect(output).to include("cleanup")
|
|
1318
|
+
end
|
|
1319
|
+
|
|
1301
1320
|
test("duplicate fixture names fail the test") do
|
|
1302
1321
|
first_fixture = Class.new(Smartest::Fixture) do
|
|
1303
1322
|
fixture(:user) { "Alice" }
|
data/smartest.gemspec
CHANGED
|
@@ -7,8 +7,15 @@ Gem::Specification.new do |spec|
|
|
|
7
7
|
spec.version = Smartest::VERSION
|
|
8
8
|
spec.authors = ["Yusuke Iwaki"]
|
|
9
9
|
|
|
10
|
-
spec.summary = "
|
|
11
|
-
spec.description =
|
|
10
|
+
spec.summary = "A Ruby test runner with pytest-style fixtures and Playwright-friendly browser testing."
|
|
11
|
+
spec.description = <<~TEXT
|
|
12
|
+
Smartest is a Ruby test runner that brings pytest-style fixture injection
|
|
13
|
+
to Ruby. Tests declare dependencies with keyword arguments, fixtures can
|
|
14
|
+
depend on other fixtures, and teardown is handled explicitly when needed.
|
|
15
|
+
|
|
16
|
+
Smartest is designed for readable Ruby tests, Rails system tests, and
|
|
17
|
+
Playwright-powered browser testing.
|
|
18
|
+
TEXT
|
|
12
19
|
spec.homepage = "https://smartest-rb.vercel.app"
|
|
13
20
|
spec.license = "MIT"
|
|
14
21
|
spec.required_ruby_version = ">= 2.7"
|
|
@@ -17,7 +24,7 @@ Gem::Specification.new do |spec|
|
|
|
17
24
|
"allowed_push_host" => "https://rubygems.org",
|
|
18
25
|
"bug_tracker_uri" => "https://github.com/YusukeIwaki/smartest/issues",
|
|
19
26
|
"changelog_uri" => "https://github.com/YusukeIwaki/smartest/blob/main/CHANGELOG.md",
|
|
20
|
-
"documentation_uri" => "https://smartest-rb.vercel.app/docs
|
|
27
|
+
"documentation_uri" => "https://smartest-rb.vercel.app/docs",
|
|
21
28
|
"homepage_uri" => spec.homepage,
|
|
22
29
|
"rubygems_mfa_required" => "true",
|
|
23
30
|
"source_code_uri" => "https://github.com/YusukeIwaki/smartest"
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: smartest
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yusuke Iwaki
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-05-
|
|
11
|
+
date: 2026-05-07 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rake
|
|
@@ -24,9 +24,13 @@ dependencies:
|
|
|
24
24
|
- - "~>"
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
26
|
version: '13.0'
|
|
27
|
-
description:
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
description: |
|
|
28
|
+
Smartest is a Ruby test runner that brings pytest-style fixture injection
|
|
29
|
+
to Ruby. Tests declare dependencies with keyword arguments, fixtures can
|
|
30
|
+
depend on other fixtures, and teardown is handled explicitly when needed.
|
|
31
|
+
|
|
32
|
+
Smartest is designed for readable Ruby tests, Rails system tests, and
|
|
33
|
+
Playwright-powered browser testing.
|
|
30
34
|
email:
|
|
31
35
|
executables:
|
|
32
36
|
- smartest
|
|
@@ -82,7 +86,7 @@ metadata:
|
|
|
82
86
|
allowed_push_host: https://rubygems.org
|
|
83
87
|
bug_tracker_uri: https://github.com/YusukeIwaki/smartest/issues
|
|
84
88
|
changelog_uri: https://github.com/YusukeIwaki/smartest/blob/main/CHANGELOG.md
|
|
85
|
-
documentation_uri: https://smartest-rb.vercel.app/docs
|
|
89
|
+
documentation_uri: https://smartest-rb.vercel.app/docs
|
|
86
90
|
homepage_uri: https://smartest-rb.vercel.app
|
|
87
91
|
rubygems_mfa_required: 'true'
|
|
88
92
|
source_code_uri: https://github.com/YusukeIwaki/smartest
|
|
@@ -104,5 +108,6 @@ requirements: []
|
|
|
104
108
|
rubygems_version: 3.4.19
|
|
105
109
|
signing_key:
|
|
106
110
|
specification_version: 4
|
|
107
|
-
summary:
|
|
111
|
+
summary: A Ruby test runner with pytest-style fixtures and Playwright-friendly browser
|
|
112
|
+
testing.
|
|
108
113
|
test_files: []
|