Ruby's Rspec has a handy option, `--only-failures`, which "filters what examples are run so that only those that failed the last time they ran are executed". https://relishapp.com/rspec/rspec-core/docs/command-line/only-failures
I'd love to have this feature in ExUnit. The closest thing I see right now is `--stale`, but if ExUnit can't accurately determine which tests may have been broken by a change, it doesn't work. (I have such an example, but don't want to be long-winded; maybe the utility of this feature is clear enough?)
--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/f5881fa3-ed51-44be-8f6b-81e5181fa449%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-core+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/2aa483e6-f63c-42d6-9e4b-84efb8adf9de%40googlegroups.com.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-core+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/270ca4ee-aa76-4e05-b7ad-c06427e748b9%40googlegroups.com.
I believe this would be a good addition. My only question is where are the failed tests stored? In _build?
For RSpec we made users configure where this state is stored, via a config.example_status_persistence_file_path option. RSpec didn’t have an established place to write that state so we left it up to the user to decide where they wanted it to go. I think for ExUnit, storing it in _build make sense.
However, note that we are not merely storing a list of failed tests. We store a list of all tests (including ones that were not included in the latest run) that looks like this:
example_id | status | run_time |
---------------------------------------------------------------------- | ------- | --------------- |
./spec/rspec/core/backtrace_formatter_spec.rb[1:1:1] | passed | 0.00115 seconds |
./spec/rspec/core/backtrace_formatter_spec.rb[1:1:2] | passed | 0.00052 seconds |
./spec/rspec/core/backtrace_formatter_spec.rb[1:1:3] | unknown | |
./spec/rspec/core/backtrace_formatter_spec.rb[1:1:4] | passed | 0.00048 seconds |
./spec/rspec/core/backtrace_formatter_spec.rb[1:2:1:1] | passed | 0.00058 seconds |
./spec/rspec/core/backtrace_formatter_spec.rb[1:2:2:1] | failed | 0.00088 seconds |
./spec/rspec/core/backtrace_formatter_spec.rb[1:2:3:1] | passed | 0.00084 seconds |
./spec/rspec/core/backtrace_formatter_spec.rb[1:3:1] | passed | 0.00052 seconds |
./spec/rspec/core/backtrace_formatter_spec.rb[1:3:2] | failed | 0.00059 seconds |
./spec/rspec/core/backtrace_formatter_spec.rb[1:4:1] | pending | 0.00053 seconds |
./spec/rspec/core/bisect/coordinator_spec.rb[1:1] | passed | 0.00366 seconds |
./spec/rspec/core/bisect/coordinator_spec.rb[1:2] | passed | 0.00307 seconds |
./spec/rspec/core/bisect/coordinator_spec.rb[1:3:1] | passed | 0.002 seconds |
./spec/rspec/core/bisect/coordinator_spec.rb[1:3:2] | passed | 0.00231 seconds |
./spec/rspec/core/bisect/coordinator_spec.rb[1:4:1] | passed | 0.00293 seconds |
./spec/rspec/core/bisect/example_minimizer_spec.rb[1:1] | passed | 0.00049 seconds |
./spec/rspec/core/bisect/example_minimizer_spec.rb[1:2] | passed | 0.0006 seconds |
# ...
This is a custom serialization format we designed to be easily scannable by a human (as it’s useful information, particular the run_time). The example_id column uniquely identifies each test (since the other common ways to identify tests, such as description and file location, are not guaranteed to be unique). Every time a test run finishes, we merge the results with the existing contents of this file using a few simple rules.
We then use this data to automatically add :last_run_status metadata to every test (with values of passed, failed, pending or unknown) when the spec files are loaded, which unlocks the generic ability to filter based on this via the RSpec CLI:
$ rspec --tag last_run_status:failed
This is the equivalent of --only failed like you asked about, José. Whether or not you add an explicit option like --only-failures is up to you, but the explicit option does provide a couple nice advantages for RSpec:
--help output. Without calling it out, it would not be clear to most users that failure filtering is possible.--only-failures is passed, we automatically load only those files. In contrast, --tag filtering doesn’t generally know anything in advance about which files might have specs matching the tag, so --tag last_run_status:failed will load all spec files, and then apply the filtering. This can be significantly slower, particularly if there are files without failures that load a heavyweight dependency (like rails).One other option we provide (which ExUnit may or may not want to provide) is --next-failure. This is the equivalent of --only-failures --fail-fast --order defined. The idea is that you often want to work through the failures systematically one-by-one. --fail-fast causes RSpec to abort as soon as the first failure is hit and --order defined disables the random ordering so you get the same failed example when you run rspec --next-failure over and over again to help you focus on a specific one. This option is also why we do the merging operation with the status from prior runs: it’s important that we preserve the failed status of tests that weren’t executed in the latest run.
ExUnit certainly doesn’t have to go the same route RSpec went here, but the combination of the perf speed up from avoiding loading files with only passing tests and the usefulness of --next-failure is pretty awesome, IMO.
--
You received this message because you are subscribed to a topic in the Google Groups "elixir-lang-core" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/elixir-lang-core/_jbuzf4UvA4/unsubscribe.
To unsubscribe from this group and all its topics, send an email to elixir-lang-core+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4J9wMEN4w3wZ4WPio%3DVvCSmgtpcdQJJsP8ggzTngnGuxw%40mail.gmail.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/CADUxQmvFXN0hkrbOc39359DboqT-W0Exxdz%2BRGUx%2B7ACXs9nfQ%40mail.gmail.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/CAK-y3Csn4Ka6e1Vu4njkmq2WZfv5QiRLfhQsej%3Db4vQEt6r0Cw%40mail.gmail.com.
I think the first step is to build the manifest itself which will give us the last_run_status information. Is that right?
I think there’s a pre-requisite you need to get out of the way before you can build the manifest: you need to decide how you plan to uniquely identify each test. Does ExUnit already have something analogous to RSpec’s example ids? If not, you could potentially use either the test name or the test location (e.g. file_name:line_number) but those may not be sufficient (for RSpec they weren’t). For RSpec, the file location is not guaranteed unique, since you can dynamically define multiple tests in a loop, which results in multiple tests sharing the same file location, and this seems like a problem for ExUnit. Likewise, RSpec does not require that each test description is unique (I think ExUnit might require this, though…is that right?). Even if test descriptions are unique, it has some properties that, IMO, make it undesirable for use here:
import or alias at the top of the module, etc).It’s easiest to explain how RSpec’s example ids work by showing an example:
# foo_spec.rb
RSpec.describe "Group 1" do
it 'foos' do # foo_spec.rb[1:1]
# ...
end
describe "a nested group" do
it 'bars' do # foo_spec.rb[1:2:1]
# ...
end
it 'bars again' do # foo_spec.rb[1:2:2]
# ...
end
end
it 'foos again' do # foo_spec.rb[1:3]
end
end
RSpec.describe "Group 2" do
it 'foos' do # foo_spec.rb[2:1]
# ...
end
end
Basically, we number each example and example group with a counter that starts over at 1 within each new scope, and use colons to separate the elements that form the “path” to the specific example. A nice thing about the ids is that they are relatively stable even in the sense of further development of the file. Users can change their test descriptions and introduce new things that change the line numbers, and the ids still work to correctly identify the tests.
Would it make sense to introduce something like this for ExUnit? In RSpec we have found these ids to be useful for several other things (including --bisect, deterministic ordering when applying a seed to a subset, etc).
BTW, this is something I’d be happy to take a stab at in ExUnit unless someone else wanted to do it.
Myron
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4LE9NLxeSxkceQuw%2BHAGEtZ3gY6jUJ3WrLAw%3D9dREJY-Q%40mail.gmail.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/CADUxQmskeA4VYJAGxEMF9j%2B4SkHWHqGU5D5J62H4QyE%3DT2DyeA%40mail.gmail.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4L1o--ChgtkkOteOB9V11Teb1mAxuL64tS6G8rAeJZEEg%40mail.gmail.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/CADUxQmv3Ge9d4U1CctBFymQv554H%3DeWmETw1t9oT7jRgJ58WGw%40mail.gmail.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/270ca4ee-aa76-4e05-b7ad-c06427e748b9%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to a topic in the Google Groups "elixir-lang-core" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/elixir-lang-core/_jbuzf4UvA4/unsubscribe.
To unsubscribe from this group and all its topics, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4J9wMEN4w3wZ4WPio%3DVvCSmgtpcdQJJsP8ggzTngnGuxw%40mail.gmail.com.
--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/CADUxQmvFXN0hkrbOc39359DboqT-W0Exxdz%2BRGUx%2B7ACXs9nfQ%40mail.gmail.com.
--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/CAK-y3Csn4Ka6e1Vu4njkmq2WZfv5QiRLfhQsej%3Db4vQEt6r0Cw%40mail.gmail.com.
--
You received this message because you are subscribed to a topic in the Google Groups "elixir-lang-core" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/elixir-lang-core/_jbuzf4UvA4/unsubscribe.
To unsubscribe from this group and all its topics, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4LE9NLxeSxkceQuw%2BHAGEtZ3gY6jUJ3WrLAw%3D9dREJY-Q%40mail.gmail.com.
--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/CADUxQmskeA4VYJAGxEMF9j%2B4SkHWHqGU5D5J62H4QyE%3DT2DyeA%40mail.gmail.com.
For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to a topic in the Google Groups "elixir-lang-core" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/elixir-lang-core/_jbuzf4UvA4/unsubscribe.
To unsubscribe from this group and all its topics, send an email to elixir-lang-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4L1o--ChgtkkOteOB9V11Teb1mAxuL64tS6G8rAeJZEEg%40mail.gmail.com.
--
You received this message because you are subscribed to the Google Groups "elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-co...@googlegroups.com.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-lang-core+unsubscribe@googlegroups.com.To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/f2a4ee00-7d44-46a2-9a63-f183bad78b68%40googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/CABKQyRqfzOemZ75ZgbV%2B9KA2djaHG%3DOqoKpYB8eNnt7zT9xJsw%40mail.gmail.com.
If I re-run only failed tests what happens if I inadvertently break something that had previously passed?
I know there's nothing to force anyone to run any test (previously passing or failing) but it seems that adding a "---only-failures" switch is facilitating something that's really not a great practice to begin with.
In addition to that, in RSpec the common use case I saw was actually abusing the feature so that randomly failing tests over CI could pass.
There is no reason it would be different here, I can think of at least one client of mine whose devs would do it immediately rather than fix the tests stability / dependency on order in first place. :(
To unsubscribe from this group and all its topics, send an email to elixir-lang-core+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4KvbDoR%2Bf7Ka9m7VbfBtGfzDYoxGGFg5CcbGYUALFKU6Q%40mail.gmail.com.
To unsubscribe from this group and all its topics, send an email to elixir-lang-core+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-lang-core/CAGnRm4KvbDoR%2Bf7Ka9m7VbfBtGfzDYoxGGFg5CcbGYUALFKU6Q%40mail.gmail.com.
Please don't misunderstand; I'm not saying "don't add the feature". I'm just saying that this sort of thing can enable a bad practice which diminishes the value of unit testing.Those of you who pointed out that running any single test as opposed to running the whole test suite is already a problem--you're completely correct. I'm not arguing against the feature at all; I'm simply trying to add an important consideration to the discussion for the sake of others who may read this discussion in the future. I'm trying to make sure that if others see this conversation in the future, they will be aware that using any subset of the tests (including "only failed") is a little bit dangerous and should always be followed as closely as possible by running the entire suite.