Skip to content
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

Apply various grammar suggestions #115

Merged
merged 1 commit into from
Nov 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 25 additions & 25 deletions src/assets/BDDInGo/BDDInGo.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,23 @@ I will not explain all principles of BDD upfront, but explain some of them as I
If you have more good resources, please post them in the comment section.

## The basic idea
I'm a fan of explaining things with real examples, that's why in [Demonstrating TDD (Test-driven development) in Go](https://dev.to/jankaritech/demonstrating-tdd-test-driven-development-in-go-27b0) I've created that small library to convert from Bikram Sambat (BS) (also called Vikram Samvat) dates to Gregorian dates and vice-versa. Now I want to use that library to create an API-driven service to do the conversion. (The project can be found on [github](https://github.com/JankariTech/bsDateServer))
I'm a fan of explaining things with real examples, that's why in [Demonstrating TDD (Test-driven development) in Go](https://dev.to/jankaritech/demonstrating-tdd-test-driven-development-in-go-27b0) I've created that small library to convert from Bikram Sambat (BS) (also called Vikram Samvat) dates to Gregorian dates and vice-versa. Now I want to use that library to create an API-driven service to do the conversion. (The project can be found on [GitHub](https://github.com/JankariTech/bsDateServer))

One could now give that "requirement" to a developer and see what happens. With that kind of small project, chances are, something good will come out, but bad things might also happen:
- the API will be super-complex and over-engineered
- the API does the conversion, but does not handle errors correctly
- etc.

So there is a lot of potential for wasted resources, conflicts, misunderstandings etc. So it would be better to write down the requirements in more detail, because:
So there is a lot of potential for wasted resources, conflicts, misunderstandings, etc. So it would be better to write down the requirements in more detail, because:

1. As customer you want your application to behave correctly (sometimes without knowing exactly what that means).
2. As developer your want to develop exactly what is requested and needed (to save time) and get paid afterwards.
3. As as QA-person, you want to know what you have to test, and you want to know what is a bug and what is a feature.
1. As a customer, you want your application to behave correctly (sometimes without knowing exactly what that means).
2. As a developer, you want to develop exactly what is requested and needed (to save time) and get paid afterward.
3. As a QA-person, you want to know what you have to test, and you want to know what is a bug and what is a feature.

So basically the goal is to get all the stakeholders (there might be more than the listed 3) to communicate and agree on what should be the acceptable behavior of the application. And that is in a nutshell the idea of BDD: improve the communication between stakeholders so that everybody knows what is talked about.
So basically the goal is to get all the stakeholders (there might be more than the listed 3) to communicate and agree on what should be the acceptable behavior of the application. And that is in a nutshell, the idea of BDD: improve the communication between stakeholders so that everybody knows what is talked about.

But how to do that? The customer might think that the one-line explanation: "API to convert dates from BS to AD and vice-versa" is enough, the manager wants to write a contract and the developer says: "code is documentation enough".
A good way to bring everybody on the same page is to describe the features of an application using the Gherkin language. Its a semi-structured language, that is so simple a cucumber could understand.
But how to do that? The customer might think that the one-line explanation: "API to convert dates from BS to AD and vice-versa" is enough, the manager wants to write a contract, and the developer says: "code is documentation enough."
A good way to bring everybody on the same page is to describe the features of an application using the Gherkin language. It's a semi-structured language that is so simple a cucumber could understand.

## Who wants to achieve what and why?
In the project folder we create a new file called `bs-to-ad-conversion.feature`. Here we want to describe the feature to convert the dates in one direction. The description of every feature of the app is supposed to go into a separate file.
Expand All @@ -55,17 +55,17 @@ Feature: convert dates from BS to AD using an API
These lines are very important. They answer the question of WHO wants to achieve WHAT with that feature and WHY. If you don't know who will use that feature, why do you implement it? If there is nothing to achieve with that feature, you actually don't have a feature. And if there is no reason to use that feature, it doesn't have a business value. So if the stakeholders (developer, customer, manager, QA, etc.) cannot answer these 3 questions, nobody really should spend time and money to implement it.

## Scenarios
Every feature has different scenarios. A "add item to shopping basket"-feature in an online-shop could have scenarios like:
Every feature has different scenarios. An "add item to shopping basket"-feature in an online-shop could have scenarios like:
- adding item to the basket while user is logged in
- adding item to the basket while user is not logged in
- adding item to the basket when the card is empty
- adding item to the basket when there is already the same item in the basket
- adding multiple items to the basket at once
- etc.

In every scenario your app might behave differently. If that specific behavior in that scenario matters for one or more stakeholders, better describe it.
In every scenario, your app might behave differently. If that specific behavior in that scenario matters for one or more stakeholders, better describe it.

In Gherkin we have to start the scenario description with the `Scenario:` keyword and a short free-text sentence:
In Gherkin, we have to start the scenario description with the `Scenario:` keyword and a short free-text sentence:

```gherkin
Scenario: converting a valid BS date
Expand All @@ -74,7 +74,7 @@ In Gherkin we have to start the scenario description with the `Scenario:` keywor
```

## Given, When, Then
Now we want to describe the specific behavior of the app in that scenario. For that Gherkin provides 3 different keywords:
Now we want to describe the specific behavior of the app in that scenario. For that, Gherkin provides 3 different keywords:
- **Given** - prerequisites for the scenario
- **When** - the action to be tested
- **Then** - the desired observable outcome
Expand All @@ -91,7 +91,7 @@ but you can use `And` (it just sounds and reads nicer)
And doing B
```

For a complex application there will be most-likely some steps to bring the application into the state that you want to test (e.g. create users, navigate to a specific page, etc), for those prerequisites you should use the `Given` keyword.
For a complex application there will be most-likely some steps to bring the application into the state that you want to test (e.g., create users, navigate to a specific page, etc.), for those prerequisites you should use the `Given` keyword.
For our app, I cannot really think of anything. So I skip over to the `When` keyword.

The `When` keyword is for the action (or multiple) you really want to test.
Expand All @@ -118,21 +118,21 @@ Now, what should happen in those specific scenarios? What is the observable outc
```

So as pieces of our description we have:
1. features - one feature per file
2. scenarios - different ways that the feature should behave
3. steps - detailed description of every scenario. Every step starts with `Given`, `When` or `Then`
1. features one feature per file
2. scenarios different ways that the feature should behave
3. steps detailed description of every scenario. Every step starts with `Given`, `When` or `Then`

All these pieces have to be written in a natural language, that all stakeholders can understand. What that means in detail would be a whole own post. In our case the "customer", requested an API, so IMO using technical terms like "HTTP-response code" should be OK. If you describe a GUI, the descriptions should be probably even less technical. The bottom line is: use words that all understand. Remember: BDD is all about improving communication!
All these pieces have to be written in a natural language that all stakeholders can understand. What that means in detail would be a whole own post. In our case, the "customer" requested an API, so IMO using technical terms like "HTTP-response code" should be OK. If you describe a GUI, the descriptions should be probably even less technical. The bottom line is: use words that all understand. Remember: BDD is all about improving communication!


For more information about how to phrase the steps definitions see: https://cucumber.io/docs/gherkin/reference/
For more information about how to phrase the step definitions see: https://cucumber.io/docs/gherkin/reference/

After specifying one feature (or even one scenario) the developer could start developing. In SCRUM-terms: one feature is one user-story, so you do all your agile development cycle with it. Create one or multiple, put them in sprints, work on them, test them, etc. The description is not only the ToDo list for the developer, but also the test-procedure for QA and the documentation.

## Test it automatically
We could stop there, but there is a great bonus-point: let's use these descriptions to run automatic tests.

For that we need software that interprets the Gherkin language and runs code that executes the tests. For Go there is the [godog package](https://github.com/cucumber/godog).
For that, we need software that interprets the Gherkin language and runs code that executes the tests. For Go there is the [godog package](https://github.com/cucumber/godog).

To install godog we fist have to create a simple `go.mod` file with the content
```golang
Expand All @@ -149,7 +149,7 @@ go get github.com/cucumber/godog@v0.12.6

(The version number `@v0.12.6` is optional, if it's not given the latest version will be installed. I set the version here to make sure this blog-post stays valid also when s.th. changes in godog)

We also, need the godog cli command to run our tests. Run the following command to add the godog cli to `$GOPATH/bin`
We also need the godog cli command to run our tests. Run the following command to add the godog cli to `$GOPATH/bin`

```shell
go install github.com/cucumber/godog/cmd/godog@v0.12.6
Expand Down Expand Up @@ -306,7 +306,7 @@ Running godog now gives us this result
...
```

It cannot connect to the server, because nothing is listening on that port. Let's change that. For a minimal implementation of a server waiting on the port put this code into `main.go` and run it with `go run main.go`
It cannot connect to the server because nothing is listening on that port. Let's change that. For a minimal implementation of a server waiting on the port put this code into `main.go` and run it with `go run main.go`
```go
package main

Expand Down Expand Up @@ -396,7 +396,7 @@ Let's do that:
}
```

Here we simply get the status code and the result body and compare it with the expectation. If it does not match, return an error. Make sure you show good error messages, the goal is to direct the developer as much as possible to the problem. The clearer the message is the quicker the developer will be able to fix the issue. Remember: these tests will not only be used during the initial development but also in the future to prevent regressions.
Here we simply get the status code and the result body and compare it with the expectation. If it does not match, return an error. Make sure you show good error messages; the goal is to direct the developer as much as possible to the problem. The clearer the message is, the quicker the developer will be able to fix the issue. Remember: these tests will not only be used during the initial development but also in the future to prevent regressions.

The regular-expression change in the `FeatureContext` just makes sure that we only accept decimal numbers in that step.

Expand Down Expand Up @@ -458,7 +458,7 @@ index ae01ed0..06299b0 100644

Basically: split the incoming string, send it to the `GoBikramSambat` lib and return the formatted result.

And with that the first scenario passes:
And with that, the first scenario passes:
```gherkin
...
Scenario: converting a valid BS date # bs-to-ad-conversion.feature:6
Expand All @@ -484,7 +484,7 @@ And with that the first scenario passes:
2.035601ms
```

With a bit of error-handling we should be able to make the other one pass also.
With a bit of error-handling, we should be able to make the other one pass also.

```diff
index 06299b0..a62eaf6 100644
Expand Down Expand Up @@ -544,7 +544,7 @@ Feature: convert dates from BS to AD using an API
```

## Examples
The scenarios we have written down are pretty limited, probably there are more requirements of the software. Specially there will be those that have not been spoken about. To reduce the size of the feature-file Gherkin has the `Examples:` keyword.
The scenarios we have written down are pretty limited, probably there are more requirements of the software. Specially, there will be those that have not been spoken about. To reduce the size of the feature-file, Gherkin has the `Examples:` keyword.

```diff
index 5a00814..18db1ed 100644
Expand Down
Loading
Loading