Test if a trace was emitted in one of the preceding steps #268
-
I've been trying to find a way how to implement the following test. For a minimal example, let's have a library with an use tracing::{info, trace};
pub fn add(left: usize, right: usize) -> usize {
trace!("{}", format!("I'm adding {left} and {right}"));
let result = left + right;
info!("The result is {result}",);
result
} Now let's specify an integration test to see if it emits the expected traces: Feature: Test if trace was emitted
Scenario Outline: Emit and check addition trace
When I call the add function with <left> and <right>
Then a trace about adding <left> and <right> is emitted
And the result <result> is written in the <level> trace
Examples:
| left | right | result | level |
| 1 | 2 | 3 | info |
| 3 | 5 | 8 | info | I've been struggling to implement this in cucumber for the past two weeks or so. I have found different kinds difficulties: ConcurrencyWorlds are constructed on a per-scenario basis and Scenarios run in parallel. Each world would have to have its own tracing subscriber registering only traces coming from the threads spawned in this world. tracing::dispatcher seems to be able to do something like that with Save the trace in the WorldEven when running each trace test Scenario alone, the biggest problem I currently have is how to make a tracing subscriber write the trace into the world. This way I could look up from a Step whether something like I have tried several different approaches, but always failed, often because of ownership and moving, global tracing subscriber. It is probably because I'm quite new to all of cucumber, tracing, and rust, but I've been hacking at this problem for dozens of hours now and still see no good way. Save the trace in a shared memory locationI have tried to write the traces into heap-allocated memory behind a Mutex (in a OnceCell), reading this from the steps, and running the test with max_concurrent_scenarios(1), but to my surprise, this has also failed when running more than a few Scenarios in a row. It's not failing in a minimal example, but in my actual library under test. (Here is my minimal example repo with this dangerously useless implementation) Write trace to a file and read itSerial scenario execution, writing and reading and reading a trace file, that's what my implementation of this will probably look like. I'd be happy to hear your Ideas how to approach this problem. |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 4 replies
-
I've been thinking about this issue a lot lately. My case is likely an example of the XY problem. I think I have to solve Y (the test of the emission of a trace), because I am ignoring a root problem X which got me in this situation in the first place. As Ian Cooper preached in his talk on TDD: Don't test implementation details, test your API. The root problem X in this case is me trying to test an implementation detail. Emitting a certain trace in a certain scenario is a feature I want my library to do, but I shall not test it because that is an implementation detail. It is not part of my public API. The library I'm writing does not communicate with its callers via traces. Rather it provides results (or Results in Rust's terms, wrapped in Ok or Err). So I should probably build my integration tests around that. |
Beta Was this translation helpful? Give feedback.
I've been thinking about this issue a lot lately. My case is likely an example of the XY problem. I think I have to solve Y (the test of the emission of a trace), because I am ignoring a root problem X which got me in this situation in the first place. As Ian Cooper preached in his talk on TDD: Don't test implementation details, test your API. The root problem X in this case is me trying to test an implementation detail. Emitting a certain trace in a certain scenario is a feature I want my library to do, but I shall not test it because that is an implementation detail. It is not part of my public API. The library I'm writing does not communicate with its callers via traces. Rather it prov…