-
-
Notifications
You must be signed in to change notification settings - Fork 28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Reusable steps across features? #93
Comments
Hi @deyanp , I don't exactly understand the question. What do you mean exactly by the reusable steps? Please elaborate and I will confirm or explain. Also, you said that the inheritance-based approach requires all steps to be in one file, I didn't understand that part too. Sorry, I may be slow to understand the context which you probably have at hand. Examples or code blocks would be helpful. |
Thanks for the quick reply, and you are right, I did not explain the issue well. Let me try:
See a sample repo at https://github.com/deyanp/XunitGherkinTests, and specifically the 2nd commit to it |
Honestly, I have no idea how F#'s inheritance works. In C#, the shared steps would be placed in a base class, and there is no limitation to put the base class in a separate file. That would result in having the same shared steps as part of the derived classes. I was always assuming that the inheritance would be the same across all .NET languages. So a couple of questions:
|
F# inheritance works the same as in C#. What I don't want to have is 1 single base class with 100 steps inside, which are shared then across 20 features/child classes. I would rather have steps grouped into several (base) classes, and reused across the 20 feature (classes). To your questions:
|
Okay, I understand more now. Thanks for explaining the split between files. I looked into the optional type extensions documentation and that looks like a way to define extension methods. However, it's an extension method and not the type itself. In other words, extension methods and partial classes are two different things. In C# for example, there is an extension method and there is partial class too. Partial class is a developer's convenience to split the class into pieces. Extension method is a way to attach a method to the class which you have not defined. This is an important difference - partial class is compiled into a single class, while extension is attached after compilation so it is a separate class (that's why it's useful when you want to attach a method to the class that you did not define - taken from the documentation). Although the syntax of F# extension looks similar to the C#'s partial, those are two different things. I also tried to find a way to define partial classes in F#, and it seems there is no way. It's a limitation of the F# language and compiler, unfortunately. So, no, you cannot split your base class across files in F#. Again, this is the F# programming language limitation and has nothing to do with the framework. Base class is one class in .NET world in general, it's only a question of whether your programming language can compile a single base class from several files. After compilation it's a single class no matter the language. |
My question was not so much about splitting F# classes/partials, but rather the following: You do have some logic in the library to find the implementation of all steps, so would it be possible to change that logic to search in the whole assembly, instead of a single feature class (hierarchy)? |
No, currently there is no way to split the steps across several feature classes. How many shared steps do you expect across your derived feature classes? And how many derived classes will you have with the same shared step? |
I am starting a new project, so no statistics yet, but on my previous project (using SpecFlow) we had about 10 000 scenarios, with about 5-10 per feature ... There were hundreds of reusable steps, and some of the steps were even "scoped" to certain features only, as there were several steps matching the same regex attribute. |
I see. This sounds like a good candidate for the implementation of the shared steps. I will keep the issue open and will work on it in the order of priority. This may take a couple of months. If you would like to contribute, or fork and implement locally for your use, feel free to do so. Sorry about the inconvenience. You are the first consumer who has such use case which is worth the implementation. |
Meanwhile, I wanted to suggest one more thing, maybe it helps: you can also chain the classes, so have a base of base of base. Each base adds several shared steps, and all features derive from the most specific base. Obviously, this is a trade-off and not ideal because you will need to split methods into hierarchy and be careful from which base you want to derive. Just a thought. |
I fully understand, time is a constraint for everybody. Thank you nevertheless for the quick interaction on the issues! P.S. Xunit.Gherkin.Quick is a really interesting project! |
Sure, thanks! I have also an additional question, which helps with implementing. If there are shared steps, how will it know where to look for? Currently, the feature class specifies the feature file path. I want to avoid scanning the entire library to find the match. Any other thoughts? |
You mean scanning the whole assembly would be slow, right. |
But consider also this: https://cucumber.io/docs/guides/anti-patterns/#feature-coupled-step-definitions |
I kind of disagree with their definition of anti-pattern. Sharing steps across scenarios seems more anti-pattern than keeping them separate. That is because (especially with the domain's specific focus in mind) each domain may understand the step differently. Sharing is not good in general in domain oriented architectures such as DDD or MDD. That said, the more I think about it, the more I understand that sharing steps would be maybe confusing, unless carefully and properly implemented. Needs to be thought through before jumping to implementation. Maybe something will become clear over time. P.S.: I am a believer that my framework should help design better systems, that's why I want to be careful. Shared steps is a dangerous field. |
Well, this is not the most straightforward topic, but
|
That introduces indirect, implicit, convention based design, which means guesswork and more issues open by confused developers. I want to avoid any "magic" and rather make things explicit, like what [FeatureFIle] attribute does. |
I am against magic myself, however I am also trying to be pragmatic. Option 1): You want to be very fast, and even assemble new scenarios without writing a single line of code. I have observed that people are happy with option 1 .. but I am not saying option 2 is a nogo, it will involve more effort (incl. maintenance) in the long run. The problem with option 1 is that people may start fighting on the Single True Step Definition (as English statement). My personal opinion is that going only with the FeatureFile (= Option 2) may work for very small test suites (and of course demo projects), however for any mid-size or big test suite you will start wondering how to quickly construct new scenarios without writing any code. |
I'll add my opinion on this. After years of using specflow with c#, I have found global steps can easily get out of control and duplication of similar functional gets created. This is hard to police when in a team of people writing tests. With xunit.gherkin.quick I have used 3 level inheritance type steps with a base steps for whole project, then an 'area' base steps then a feature steps. The steps files are normally just a few lines per step that use other classes that are reusable. This makes it easy to debug as all steps are in 1-3 steps files. |
That was useful, @glassenbury! |
There's always going to be basic stuff like "login as this persona", "make an authenticated request to this endpoint", "lookup xyz common data". But seems fine to have that in an abstract class |
I wanted to briefly chime in and clarify that the code reuse across features is possible via two options: either via the base class or via dependency injection (because Xunit supports it). I was able to write up documentation covering both approaches here: https://github.com/ttutisani/Xunit.Gherkin.Quick/blob/master/docs/reuse-step-implementation-across-features.md I hope this helps. |
Can you confirm that currently it is not possible to define reusable steps in a separate implementation file, to which several features can be mapped?
I see that there is an inheritance-based approach chosen, however it requires all steps to be in 1 file, and at a first glance does not allow to have several features "sharing" them ...
The text was updated successfully, but these errors were encountered: