Hi,
What is the best practices for serenity-js?
especially for the directory structure
from serenity-js-getting-started it have page object in ui
but the feature and test file all in one directory
what is the best practices for this?
thx
Hi @tremkonzah and apologies for being slow to reply on this.
As per our email conversation, "acceptance testing best practices" is a huge topic.
I started answering some of your questions in the "Serenity/JS Handbook" that's now available at serenity-js.org
I made a recommendation for some basic structure in the updated version of the "From Scripts to Serenity" tutorial.
Assuming you keep the test code under spec, and your application uses some imaginary "date picker" UI widget, you could organise your codebase _by type_.
This is a very simple structure that puts the ui components together in one directory, the tasks in another one and so on. This conceptually resembles the design of many MVC frameworks, where you'd have all the controllers in one place, all the models in another.
โโโ screenplay
โโโ components
โ โโโ date_picker.ts <- contains targets and questions
โ โโโ ... <- other ui components, like "login_form.ts", "basket.ts" and so on
โโโ tasks
โโโ open_date_picker.ts
โโโ close_date_picker.ts
โโโ pick_a_date.ts
โโโ ... <- other tasks related to other widgets, like "add_a_product.ts"
tasks. This could negatively affect discoverability of your code.Having said that, you can always start with this structure and refactor to a different one when changing it adds value.
One of the many benefits of using TypeScript is that it'll tell you straight away if by moving files around you've broken anything. It just won't compile :-)
Once the range and variation of the UI components you're working with starts to stabilise, you can try to slice the codebase _by component_.
If we take our imaginary date picker example again, we could propose the following structure:
โโโ screenplay
โโโ components
โ โโโ date_picker
โ โ โโโ components
โ โ โ โโโ date_picker.ts <- contains targets and questions
โ โ โโโ index.ts <- exposes the tasks to test scenarios
โ โ โโโ tasks
โ โ โโโ open_date_picker.ts
โ โ โโโ close_date_picker.ts
โ โ โโโ pick_a_date.ts
โ โ
โ โโโ ... <- other ui components, like "login_form.ts", "basket.ts" and so on
โโโ tasks
โโโ ... <- other tasks related to other widgets, like "add_a_product.ts"
index.ts only exposes the tasks, not the definition of the component structure (targets and questions)There are several other ways to do it though, and I just realised I should write a post about it ;-)
To summarise, start with slicing by type, and extract components when you learn more about them.
Hope this helps!
Jan
The file organization under:
https://github.com/jan-molak/serenity-js-getting-started/tree/2-reports/src/screenplay
is different from:
https://github.com/serenity-js/tutorial-from-scripts-to-serenity/tree/2-reports/spec/screenplay
which is considered current best practice and why?
My current approach to organising the files relies on continuous evolution and refactoring and could be summarised like this:
screenplay.ts where I put all the tasks, interactions, questions and page objects. This helps me to get started with running the scenarios quickly without worrying too much about the structure at this stage:โโโ screenplay.ts
screenplay.ts, so that file is rather short-lived. Also, I use the Task.where(), Interaction.where(), Question.about() syntax to keep the definitions short and compact.screenplay.ts and a couple of logical components identified, I create files for those components where I move tasks and page objects that deal only with those specific components.โโโ basket.ts <- task, questions, interactions and page objects for the basket widget
โโโ date_picker.ts <- task, questions, interactions and page objects for date picker widget
โโโ screenplay.ts
โโโ basket <- sub-components of the basket component
โ โโโ saved_for_later.ts
โ โโโ summary.ts
โ โโโ recently_viewed.ts
โ โโโ screenplay.ts <- anything basket-related that doesn't fit within any of the sub-components
โโโ date_picker.ts
โโโ screenplay.ts
screenplay.ts files will only contain tasks as questions, interactions and page objects will be placed together with their respective components. If that's the case I rename screenplay.ts to tasks.ts as it only has one responsibility of defining the tasks overarching several components. โโโ basket
โ โโโ saved_for_later.ts
โ โโโ summary.ts
โ โโโ recently_viewed.ts
โ โโโ tasks.ts <- tasks overarching several sub-components
โโโ date_picker.ts
โโโ tasks.ts <- tasks overarching several components
tasks.ts files to be too large - I split them further and create a file per task:โโโ basket
โ โโโ saved_for_later.ts
โ โโโ summary.ts
โ โโโ recently_viewed.ts
โ โโโ tasks <- tasks overarching several sub-components
โ โโโ AddProduct.ts
โ โโโ ChangeQuantity.ts
โ โโโ RemoveProduct.ts
โ โโโ ...
โโโ date_picker.ts
โโโ tasks.ts
โโโ basket
โ โโโ saved_for_later <- page objects, interactions, questions live in separate "packages"
โ โ โโโ ui.ts
โ โ โโโ interactions.ts
โ โ โโโ questions.ts
โ โ โโโ tasks.ts
โ โโโ summary.ts
โ โโโ recently_viewed.ts
โ โโโ tasks
โ โโโ AddProduct.ts
โ โโโ ChangeQuantity.ts
โ โโโ RemoveProduct.ts
โ โโโ ...
โโโ date_picker.ts
โโโ tasks.ts
As you can see, this is an evolutionary process where the directory structure evolves together with my growing understanding of the product.
Does that help?
Jan
Most helpful comment
Hi @tremkonzah and apologies for being slow to reply on this.
As per our email conversation, "acceptance testing best practices" is a huge topic.
I started answering some of your questions in the "Serenity/JS Handbook" that's now available at serenity-js.org
I made a recommendation for some basic structure in the updated version of the "From Scripts to Serenity" tutorial.
Slice by type
Assuming you keep the test code under
spec, and your application uses some imaginary "date picker" UI widget, you could organise your codebase _by type_.This is a very simple structure that puts the ui components together in one directory, the tasks in another one and so on. This conceptually resembles the design of many MVC frameworks, where you'd have all the controllers in one place, all the models in another.
tasks. This could negatively affect discoverability of your code.Having said that, you can always start with this structure and refactor to a different one when changing it adds value.
One of the many benefits of using TypeScript is that it'll tell you straight away if by moving files around you've broken anything. It just won't compile :-)
Slice by component
Once the range and variation of the UI components you're working with starts to stabilise, you can try to slice the codebase _by component_.
If we take our imaginary date picker example again, we could propose the following structure:
index.tsonly exposes the tasks, not the definition of the component structure (targets and questions)There are several other ways to do it though, and I just realised I should write a post about it ;-)
To summarise, start with slicing by type, and extract components when you learn more about them.
Hope this helps!
Jan