Skip to content

Commit fce0e08

Browse files
committed
Resolve #225 - Add sample task docs
1 parent 9ec60e7 commit fce0e08

2 files changed

Lines changed: 43 additions & 7 deletions

File tree

CSF.Screenplay.Docs/docs/writingPerformables/AvoidBranchingLogic.md

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,22 @@
33
[Performances] are written _a lot like scripts_.
44
Where possible, [Performables] should avoid branching or looping logic.
55
_This is particularly true_ when Screenplay is being used [as a tool for testing].
6-
Good test logic has [a cyclomatic complexity] of precisely one.
6+
Good test logic has [a cyclomatic complexity] as close to one as possible.
77

88
Sometimes looping logic is unavoidable and desirable in a Screenplay, imagine a performable which has an [Actor] repeat a process `N` times.
9-
This is acceptable if used judiciously.
9+
Sometimes branching logic is required to deal with external systems which present differing behaviour.
10+
The [Selenium Extension] makes use of this technique to accomodate different web browsers and web driver implementations.
11+
Such logic is acceptable if used judiciously.
1012

11-
Performables should always avoid branching logic like `if` or `switch` though, and _should never_ contain such logic when being used for tests.
12-
If more than one mode of operation is required then write more than one performable.
13-
The path through a Performance should be completely deterministic, short of an unexpected error or failure.
13+
Perhaps the best guideline is for the developer to ask themselves:
14+
15+
> Could I separate this into two or more tasks and decide which I need at the point of usage?
16+
17+
If the answer is "yes", then it's often better to do just that, rather than write a multi-function Task.
1418

1519
[Performances]: xref:CSF.Screenplay.IPerformance
1620
[Performables]: ../../glossary/Performable.md
1721
[as a tool for testing]: ../bestPractice/SuitabilityAsATestingTool.md
1822
[a cyclomatic complexity]: https://en.wikipedia.org/wiki/Cyclomatic_complexity
1923
[Actor]: xref:CSF.Screenplay.Actor
24+
[Selenium Extension]: ../extensions/selenium/index.md

CSF.Screenplay.Docs/docs/writingPerformables/index.md

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,40 @@ On the other hand, it is very likely that developers will need to write [Tasks],
1212

1313
## A sample Task
1414

15-
Here is an annotated example of a [Task] which makes use of a fictitious Action and a fictitious Question.
15+
Here is an annotated example of a [Task] which makes use of a fictitious Action and Question.
1616

17-
TODO: Write this docco
17+
```csharp
18+
// A parameter is constructor injected, so that this task may be parameterised.
19+
// This is entirely optional, if no parameters are needed then none need be added.
20+
public class BuyMilkIfNeeded(int cupsOfCoffee) : IPerformable, ICanReport
21+
{
22+
// Always implement the ICanReport interface if possible.
23+
// Write a human-readable report of what the Task does.
24+
// There's no need to detail the work done by consumed performables; they will get their own reports.
25+
public ReportFragment GetReportFragment(Actor actor, IFormatsReportFragment formatter)
26+
=> formatter.Format("{Actor} adds milk to their shopping list, if they need it to make {Cups} cup(s) of coffee", actor, cupsOfCoffee);
27+
28+
public async ValueTask PerformAsAsync(ICanPerform actor, CancellationToken cancellationToken = default)
29+
{
30+
// Use of a fictitious Question, remember to await it and to pass the cancellation token.
31+
// This question, which looks in the refridgerator and gets a decimal which represents the
32+
// quantity of an item inside, measured in litres, is ficitious.
33+
// It is consumed here from a fictitious builder method.
34+
var litresOfMilk = await actor.PerformAsync(LookInTheRefridgeratorFor("Milk").AndTellMeHowMuchRemainsInLitres(), cancellationToken);
35+
var isEnough = IsThisEnoughMilkToMakeCoffee(cupsOfCoffee, litresOfMilk);
36+
if(!isEnough)
37+
// Use of a fictitious Action, again remember to await it and pass the cancellation token.
38+
await actor.PerformAsync(AddToMyShoppingList("Milk"), cancellationToken);
39+
40+
}
41+
42+
static bool IsThisEnoughMilkToMakeCoffee(int cupsOfCoffee, decimal litresOfMilk)
43+
{
44+
// Implementation omitted, determines if the litresOfMilk param is enough milk to make
45+
// the indicated number of cupsOfCoffee.
46+
}
47+
}
48+
```
1849

1950
[Task]: ../../glossary/Task.md
2051

0 commit comments

Comments
 (0)