olexale / bdd_widget_test

A BDD-style widget testing library
MIT License
101 stars 30 forks source link

World within scenario #62

Closed daniel-deboeverie-lemon closed 9 months ago

daniel-deboeverie-lemon commented 9 months ago

I wish to add something like the world in cucumber.js (https://github.com/cucumber/cucumber-js/blob/main/docs/support_files/world.md)

This would allow for state to pass between steps within a scenario.

I would recommend the addition of a flag that either enables or disables this. In the scenario you then create an object that contains a Map<String, dynamic> that is passed along in the scenarios.

By wrapping this map in an object it will be able to expand it later (with for example logs like the cucumber.js has). I think of using a key value map since this seems the most flexible for sharing whatever state you need.

olexale commented 9 months ago

Hey @daniel-deboeverie-lemon 👋 First of all, thanks for the proposal and the PR!

I see that the proposed change adds some complexity to the package, so it could make it harder to maintain, hence I'd love to understand the use case better before merging the PR.

bdd_widget_test was created as a dumb converter from human to Flutter's widget tests. To keep it simple, I'm trying to keep it as close as possible to plain tests one could write, as a developer, manually. From the top of my head, I see the following options to pass some data between steps in my tests without Gherkin/BDD:

  1. Pass the data as a parameter to function (similar to the solution you implemented). With this option, I'm getting pure functions and predictability. Unfortunately, if we go the World route, parameters will be stored in Map<String, dynamic> which is not type-safe. The alternative is to create different Worlds, but that would make steps non-reusable between projects, as there might be conflicts because of different implementations.
  2. Use global singleton objects to hold the data. While these singletons provide type safety, they make steps harder to maintain. Plus, now developers are responsible for clearing holders between tests, which is easy to forget or miss.
  3. Use DI. Frankly, conceptually that's similar to option 2, but a bit more robust. In my tests, I use get_it. That allows me to reuse steps between different projects, keeping the implementation type-safe. As I use get_it in my apps as well, reset cleans global states the same moment it resets the state of other singletons that potentially might be implemented in the app.

While option 1 is elegant, as I said, it adds some complexity to the package, plus introduces a new concept that might be useful in the future or might become redundant if there are no other use cases for it. Option 2 is poorly scalable, but option 3 works for me quite well. The final solution looks quite similar to what is described here for Java/Kotlin.

Do you think option 3 might always be used instead of 1, or maybe I'm missing some edge cases? What is your vision of the World class future, do you think it may evolve into something more than just a map of parameters?

daniel-deboeverie-lemon commented 9 months ago

Outside of the fact that you would need to call GetIt.I.reset() at the end of every test, it is indeed a clean solution. I was worried it might run into problems with multiple tests running concurrently, but I wasn't able to break it.

Something I imagine there might be a request for later is the ability to add logs into the world, but I think you could also do this in GetIt.

I might use this instead of the world variable I proposed. The type safety was indeed a big issue.

Thx for the input!

olexale commented 9 months ago

the ability to add logs into the world

Would you please give more details on this?

daniel-deboeverie-lemon commented 9 months ago

In cucumber js the logs parameter of the world is basically just a string that logs can be dumped into. With the framework we used in our project, this log also got filled by said framework, thus allowing us to use their logs in our rapport.

Since we want to keep this package as dumb as possible, and I don't see an immediate use case for us to log from the code, I don't think this would be truly used.

olexale commented 9 months ago

Hey @daniel-deboeverie-lemon, How everything is going with your experiment of using DI instead of World? Have you found any issues with this approach? If not, do you think we may close this ticket?

daniel-deboeverie-lemon commented 9 months ago

Hey @olexale, DI has worked. Thank you for the hint :)

olexale commented 9 months ago

Alrighty, as we have a solution that does the trick and is simpler to maintain, I'm closing the issue and the appropriate PR. @daniel-deboeverie-lemon, thank you so much for the proposition!