From 12430c3cb04f85e8925c2cf06857beb8beac46c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roger=20Vi=C3=B1as=20Alcon?= Date: Wed, 22 Nov 2023 20:58:31 +0100 Subject: [PATCH] More docs (#40) * Format * More doc * Improve * More * More * More * More --- bash/README.md | 74 +++++++++------- dotnet/README.md | 187 +++++++++++++++++++++++++++++++++++----- go/HelloApp.go | 6 +- go/HelloApp_test.go | 34 ++++---- go/HelloConsole.go | 4 +- go/HelloMessage.go | 4 +- go/HelloMessage_test.go | 10 +-- go/Main.go | 8 +- go/README.md | 171 +++++++++++++++++++++++++++++++++--- 9 files changed, 401 insertions(+), 97 deletions(-) diff --git a/bash/README.md b/bash/README.md index 174204d..1a45bd0 100644 --- a/bash/README.md +++ b/bash/README.md @@ -6,29 +6,32 @@ [Bash](https://www.gnu.org/software/bash/) testing with [BATS](https://bats-core.readthedocs.io/en/stable/#) ⚠️ This project includes **BATS** git submodules so once you clone the repo you will need to init and update them: + ``` git submodule init git submodule update ``` -* [Show me the code](#show-me-the-code) - * [Implementation](#implementation) - * [Test](#test) -* [Run this project using 🐳 docker](#run-this-project-using--docker) -* [Run this project locally](#run-this-project-locally) +- [Show me the code](#show-me-the-code) + - [Implementation](#implementation) + - [Test](#test) +- [Run this project using 🐳 docker](#run-this-project-using--docker) +- [Run this project locally](#run-this-project-locally) ## Show me the code ### Implementation -1) Create `helloMessage` function in [src/hello-message.bash](src/hello-message.bash): +1. Create `helloMessage` function in [src/hello-message.bash](src/hello-message.bash): + ```shell function helloMessage() { echo "Hello World!" } ``` -2) Create `helloConsole` function in [src/hello-console.bash](src/hello-console.bash): +2. Create `helloConsole` function in [src/hello-console.bash](src/hello-console.bash): + ```shell function helloConsole() { local text=$1 @@ -36,7 +39,8 @@ function helloConsole() { } ``` -3) Create `helloApp` function in [src/hello-app.bash](src/hello-app.bash): +3. Create `helloApp` function in [src/hello-app.bash](src/hello-app.bash): + ```shell function helloApp() { local messageFn=$1 @@ -47,7 +51,8 @@ function helloApp() { Note that `helloApp` function receives the two other functions as parameters and just executes them. -4) Create a main script [src/hello-bash](src/hello.bash) that just loads the 3 required scripts and executes `helloApp` passing `helloMessage` and `helloConsole` functions as parameters: +4. Create a main script [src/hello-bash](src/hello.bash) that just loads the 3 required scripts and executes `helloApp` passing `helloMessage` and `helloConsole` functions as parameters: + ```shell source "$(dirname "${BASH_SOURCE[0]}")/hello-message.bash" source "$(dirname "${BASH_SOURCE[0]}")/hello-console.bash" @@ -58,11 +63,12 @@ helloApp helloMessage helloConsole ### Test -Based on [BATS Tutorial > Your first test](https://bats-core.readthedocs.io/en/stable/tutorial.html#your-first-test) ... +Following [BATS Tutorial > Your first test](https://bats-core.readthedocs.io/en/stable/tutorial.html#your-first-test) ... + +1. For simplicity create all tests in [test/hello.bats](test/hello.bats) file -1) For simplicity we create all tests in [test/hello.bats](test/hello.bats) file +2. Configure current directory and load some helper modules in `setup` function: -2) We configure current directory and load some helper modules in `setup` function: ```shell setup() { load 'test_helper/bats-support/load' @@ -73,7 +79,8 @@ setup() { } ``` -3) We test `helloMessage` function using [assert_output](https://github.com/bats-core/bats-assert#assert_output) helper: +3. Test `helloMessage` function using [assert_output](https://github.com/bats-core/bats-assert#assert_output) helper: + ```shell @test "helloMessage should return hello world" { source hello-message.bash @@ -83,7 +90,8 @@ setup() { } ``` -4) We test `helloApp` function creating mock functions for `helloMessage` and `helloConsole`: +4. Test `helloApp` function creating mock functions for `helloMessage` and `helloConsole`: + ```shell @test "helloApp should print hello message" { function helloMessageMock() { @@ -102,7 +110,8 @@ setup() { } ``` -5) We test the whole `hello.bash` script too: +5. Test the whole `hello.bash` script too: + ```shell @test "hello.bash should print hello world" { run hello.bash @@ -111,7 +120,8 @@ setup() { } ``` -6) Test output should look like: +6. Test output should look like: + ``` hello.bats ✓ helloMessage should return hello world @@ -124,26 +134,30 @@ hello.bats Take a look at the other [Libraries and Add-ons](https://bats-core.readthedocs.io/en/stable/writing-tests.html#libraries-and-add-ons) that may be useful in the future. For example, there is a couple of **bats-mock** libraries that can be used to mock programs (but unfortunately not able to mock functions). ## Run this project using 🐳 [docker](https://www.docker.com/) -* Execute `./docker-run.sh` -* Once inside the container: - * Test with `./test/bats/bin/bats test/hello.bats` - * Run with `./src/hello.bash` + +- Execute `./docker-run.sh` +- Once inside the container: + - Test with `./test/bats/bin/bats test/hello.bats` + - Run with `./src/hello.bash` ## Run this project locally ### Pre-requisites -* Ensure you have **bash** installed `bash --version` (this project uses version 5.2) + +- Ensure you have **bash** installed `bash --version` (this project uses version 5.2) ### Create project from scratch -* Create an empty project with `src` and `test` folders -* Follow [Quick installation](https://bats-core.readthedocs.io/en/stable/tutorial.html#quick-installation) guide to install **BATS** submodules: - ``` - git submodule add https://github.com/bats-core/bats-core.git test/bats - git submodule add https://github.com/bats-core/bats-support.git test/test_helper/bats-support - git submodule add https://github.com/bats-core/bats-assert.git test/test_helper/bats-assert - ``` + +- Create an empty project with `src` and `test` folders +- Follow [Quick installation](https://bats-core.readthedocs.io/en/stable/tutorial.html#quick-installation) guide to install **BATS** submodules: + +``` +git submodule add https://github.com/bats-core/bats-core.git test/bats +git submodule add https://github.com/bats-core/bats-support.git test/test_helper/bats-support +git submodule add https://github.com/bats-core/bats-assert.git test/test_helper/bats-assert +``` ### Run locally -* Test with `./test/bats/bin/bats test/hello.bats` -* Run with `./src/hello.bash` +- Test with `./test/bats/bin/bats test/hello.bats` +- Run with `./src/hello.bash` diff --git a/dotnet/README.md b/dotnet/README.md index d7a03d8..cc6a218 100644 --- a/dotnet/README.md +++ b/dotnet/README.md @@ -5,34 +5,175 @@ [.NET C#](https://learn.microsoft.com/dotnet/csharp) testing with [NUnit](https://nunit.org/) and [Moq](https://www.devlooped.com/moq/) +- [Show me the code](#show-me-the-code) + - [Implementation](#implementation) + - [Test](#test) +- [Run this project using 🐳 docker](#run-this-project-using--docker) +- [Run this project locally](#run-this-project-locally) + +## Show me the code + +### Implementation + +1. Create `HelloMessage` interface and `HelloWorldMessage` implementing it in [Hello.Main/HelloMessage.cs](Hello.Main/HelloMessage.cs): + +```csharp +public interface HelloMessage +{ + public String Text { + get; + } +} + +public class HelloWorldMessage : HelloMessage +{ + public string Text { + get { + return "Hello World!"; + } + } +} +``` + +Creating it as an interface will allow us to mock it for testing using [Moq](https://www.devlooped.com/moq/) which does not support mocking final classes. Maybe other libraries support that but using an interface is simpler. + +2. Same way create `HelloConsole` interface and `HelloSystemConsole` class implementing it in [Hello.Main/HelloConsole.cs](Hello.Main/HelloConsole.cs): + +```csharp +public interface HelloConsole { + void Print(String text); +} + +public class HelloSystemConsole : HelloConsole +{ + public void Print(String text) { + Console.WriteLine(text); + } +} +``` + +4. Create `HelloApp` in [Hello.Main/HelloApp.cs](Hello.Main/HelloApp.cs): + +```csharp +public class HelloApp +{ + private HelloMessage message; + private HelloConsole console; + + public HelloApp(HelloMessage message, HelloConsole console) { + this.message = message; + this.console = console; + } + + public void PrintHello() { + console.Print(message.Text); + } +} +``` + +5. Create main [Hello.Main/Program.cs](Hello.Main/Program.cs) that wraps it all together: + +```csharp +var message = new HelloWorldMessage(); +var console = new HelloSystemConsole(); +var app = new HelloApp(message, console); +app.PrintHello(); +``` + +### Test + +Following [NUnit > Writing Tests](https://docs.nunit.org/articles/nunit/writing-tests/attributes.html) guide ... + +1. Test `HelloMessage` in [Hello.Test/HelloMessageTest.cs](Hello.Test/HelloMessageTest.cs): + +```csharp +[Test] +public void ShouldReturnHelloWorld() +{ + var message = new HelloWorldMessage(); + Assert.That(message.Text, Is.EqualTo("Hello World!")); +} +``` + +2. Test `HelloApp` in [Hello.Test/HelloAppTest.cs](Hello.Test/HelloAppTest.cs): + +```csharp +[Test] +public void ShouldPrintHelloMessage() +{ + var messageText = "Hello Test!"; + + // 2.1 Create a mock of HelloMessage + var messageMock = new Mock(); + // - Expect HelloMessage mock to receive a call to .Text + // and return "Hello Test!" + messageMock.Setup(message => message.Text).Returns(messageText); + // Get the mock object to pass it to HelloApp + var message = messageMock.Object; + + // 2.2 Create a mock of HelloConsol + var consoleMock = new Mock(); + // - No need to set expectations for this one + // - Get the mock object to pass it to HelloApp + var console = consoleMock.Object; + + // 2.3 Create a HelloApp, the one we want to test, passing the mocks + var app = new HelloApp(message, console); + // - Execute the method we want to test + app.PrintHello(); + + // 2.4 Verify HelloConsole mock has received one time + // a call to .Print with "Hello Test!" + consoleMock.Verify(console => console.Print(messageText), Times.Once); +} +``` + +3. Test output should look like: + +``` +NUnit Adapter 4.2.0.0: Test execution complete + Passed ShouldPrintHelloMessage [180 ms] + Passed ShouldReturnHelloWorld [7 ms] + +Test Run Successful. +Total tests: 2 + Passed: 2 + Total time: 2.7702 Seconds +``` + ## Run this project using 🐳 [docker](https://www.docker.com/) -* Execute `./docker-run.sh` -* Once inside the container: - * Test with `dotnet test -v quiet -l:"console;verbosity=normal"` - * Run with `dotnet run --project Hello.Main` - * Build with `dotnet publish -c Release` + +- Execute `./docker-run.sh` +- Once inside the container: + - Test with `dotnet test -v quiet -l:"console;verbosity=normal"` + - Run with `dotnet run --project Hello.Main` + - Build with `dotnet publish -c Release` ## Run this project locally ### Pre-requisites -* Install [.NET](https://dotnet.microsoft.com/download) - * Check [.NET CLI](https://learn.microsoft.com/dotnet/core/tools/) executing `dotnet --version` -### Run locally -* Test with `dotnet test -v quiet -l:"console;verbosity=normal"` -* Run with `dotnet run --project Hello.Main` -* Build with `dotnet publish -c Release` +- Install [.NET](https://dotnet.microsoft.com/download) + - Check [.NET CLI](https://learn.microsoft.com/dotnet/core/tools/) executing `dotnet --version` ### Create project from scratch -* Execute these commands: - ``` - dotnet new sln --name Hello - - dotnet new console --language "C#" --framework net8.0 --name Hello.Main - - dotnet new nunit --language "C#" --framework net8.0 --name Hello.Test - dotnet add ./Hello.Test reference ./Hello.Main - dotnet add ./Hello.Test package Moq --version 4.20.69 - - dotnet sln Hello.sln add ./Hello.Main ./Hello.Test - ``` + +- Execute these commands: + +``` +dotnet new sln --name Hello + +dotnet new console --language "C#" --framework net8.0 --name Hello.Main + +dotnet new nunit --language "C#" --framework net8.0 --name Hello.Test +dotnet add ./Hello.Test reference ./Hello.Main +dotnet add ./Hello.Test package Moq --version 4.20.69 + +dotnet sln Hello.sln add ./Hello.Main ./Hello.Test +``` + +### Run locally + +- Test with `dotnet test -v quiet -l:"console;verbosity=normal"` +- Run with `dotnet run --project Hello.Main` +- Build with `dotnet publish -c Release` diff --git a/go/HelloApp.go b/go/HelloApp.go index 58aa5a4..9ff64b8 100644 --- a/go/HelloApp.go +++ b/go/HelloApp.go @@ -1,10 +1,10 @@ package main type HelloApp struct { - message HelloMessage - console HelloConsole + message HelloMessage + console HelloConsole } func (app *HelloApp) PrintHello() { - app.console.Print(app.message.Text()) + app.console.Print(app.message.Text()) } diff --git a/go/HelloApp_test.go b/go/HelloApp_test.go index c1238f1..473ddfa 100644 --- a/go/HelloApp_test.go +++ b/go/HelloApp_test.go @@ -3,37 +3,37 @@ package main import "testing" type HelloMessageMock struct { - text string + text string } func (message *HelloMessageMock) Text() string { - return message.text + return message.text } type HelloConsoleMock struct { - Calls int - Text string + Calls int + Text string } func (console *HelloConsoleMock) Print(text string) { - console.Calls++ - console.Text = text + console.Calls++ + console.Text = text } func TestShouldReturnPrintHelloMessage(t *testing.T) { - messageText := "Hello Test!" - message := HelloMessageMock{messageText} + messageText := "Hello Test!" + message := HelloMessageMock{messageText} - console := HelloConsoleMock{} + console := HelloConsoleMock{} - app := HelloApp{&message, &console} - app.PrintHello() + app := HelloApp{&message, &console} + app.PrintHello() - if console.Calls != 1 { - t.Fatalf("HelloConsole expected calls 1 but got %d", console.Calls) - } + if console.Calls != 1 { + t.Fatalf("HelloConsole expected calls 1 but got %d", console.Calls) + } - if console.Text != messageText { - t.Fatalf("HelloConsole expected text %s but got %s", messageText, console.Text) - } + if console.Text != messageText { + t.Fatalf("HelloConsole expected text %s but got %s", messageText, console.Text) + } } diff --git a/go/HelloConsole.go b/go/HelloConsole.go index e2eb3fd..ecbb272 100644 --- a/go/HelloConsole.go +++ b/go/HelloConsole.go @@ -3,11 +3,11 @@ package main import "fmt" type HelloConsole interface { - Print(text string) + Print(text string) } type HelloSystemConsole struct{} func (console *HelloSystemConsole) Print(text string) { - fmt.Println(text) + fmt.Println(text) } diff --git a/go/HelloMessage.go b/go/HelloMessage.go index 8b8d362..53e7143 100644 --- a/go/HelloMessage.go +++ b/go/HelloMessage.go @@ -1,11 +1,11 @@ package main type HelloMessage interface { - Text() string + Text() string } type HelloWorldMessage struct{} func (message *HelloWorldMessage) Text() string { - return "Hello World!" + return "Hello World!" } diff --git a/go/HelloMessage_test.go b/go/HelloMessage_test.go index 40e00f4..1acb787 100644 --- a/go/HelloMessage_test.go +++ b/go/HelloMessage_test.go @@ -3,9 +3,9 @@ package main import "testing" func TestShouldReturnHelloWorld(t *testing.T) { - messageText := "Hello World!" - message := HelloWorldMessage{} - if message.Text() != messageText { - t.Fatalf("Expected %s but got %s", messageText, message.Text()) - } + messageText := "Hello World!" + message := HelloWorldMessage{} + if message.Text() != messageText { + t.Fatalf("Expected %s but got %s", messageText, message.Text()) + } } diff --git a/go/Main.go b/go/Main.go index 7ae6b24..694e268 100644 --- a/go/Main.go +++ b/go/Main.go @@ -1,8 +1,8 @@ package main func main() { - message := HelloWorldMessage{} - console := HelloSystemConsole{} - app := HelloApp{&message, &console} - app.PrintHello() + message := HelloWorldMessage{} + console := HelloSystemConsole{} + app := HelloApp{&message, &console} + app.PrintHello() } diff --git a/go/README.md b/go/README.md index 76a69e1..5d1746b 100644 --- a/go/README.md +++ b/go/README.md @@ -3,24 +3,173 @@ # Go -[Go](https://go.dev/) testing with core libraries +[Go](https://go.dev/) testing with [Standard library](https://pkg.go.dev/testing) + +- [Show me the code](#show-me-the-code) + - [Implementation](#implementation) + - [Test](#test) +- [Run this project using 🐳 docker](#run-this-project-using--docker) +- [Run this project locally](#run-this-project-locally) + +## Show me the code + +### Implementation + +1. Create `HelloMessage` interface and `HelloWorldMessage` implementation in [HelloMessage.go](HelloMessage.go): + +```go +type HelloMessage interface { + Text() string +} + +type HelloWorldMessage struct{} + +func (message *HelloWorldMessage) Text() string { + return "Hello World!" +} +``` + +Creating it as an interface will allow us to mock it for testing. + +2. Same way create `HelloConsole` interface and `HelloSystemConsole` implementation in [HelloConsole.go](HelloConsole.go): + +```go +type HelloConsole interface { + Print(text string) +} + +type HelloSystemConsole struct{} + +func (console *HelloSystemConsole) Print(text string) { + fmt.Println(text) +} +``` + +3. Create `HelloApp` class in [HelloApp.go](HelloApp.go): + +```go +type HelloApp struct { + message HelloMessage + console HelloConsole +} + +func (app *HelloApp) PrintHello() { + app.console.Print(app.message.Text()) +} +``` + +4. Create main function in [Main.go](Main.go) that wraps it all together: + +```go +func main() { + message := HelloWorldMessage{} + console := HelloSystemConsole{} + app := HelloApp{&message, &console} + app.PrintHello() +} +``` + +### Test + +Following [Standard library > testing](https://pkg.go.dev/testing) guide ... + +1. Test `HelloMessage` in [HelloMessage_test.go](HelloMessage_test.go): + +```go +func TestShouldReturnHelloWorld(t *testing.T) { + messageText := "Hello World!" + message := HelloWorldMessage{} + if message.Text() != messageText { + t.Fatalf("Expected %s but got %s", messageText, message.Text()) + } +} +``` + +2. Test `HelloApp` in [HelloApp_test.go](HelloApp_test.go): + +```go +// 2.1 Define a HelloMessageMock struct that ... +type HelloMessageMock struct { + text string +} + +// ... fullfils HelloMessage interface +func (message *HelloMessageMock) Text() string { + return message.text +} + +// 2.2 Define a HelloConsoleMock that ... +type HelloConsoleMock struct { + Calls int + Text string +} + +// ... fullfills HelloConsole interface +func (console *HelloConsoleMock) Print(text string) { + console.Calls++ + console.Text = text +} + +func TestShouldReturnPrintHelloMessage(t *testing.T) { + // 2.3 Create a HelloMessageMock + // - It will return "Hello Test!" + messageText := "Hello Test!" + message := HelloMessageMock{messageText} + + // 2.4 Create a HelloConsoleMock + // - It will capture the calls made + console := HelloConsoleMock{} + + // 2.5 Create a HelloApp, the one we want to test, passing the mocks + app := HelloApp{&message, &console} + // - Execute the method we want to test + app.PrintHello() + + // 2.6 Assert HelloConsoleMock has been called once + if console.Calls != 1 { + t.Fatalf("HelloConsole expected calls 1 but got %d", console.Calls) + } + + // 2.7 Assert HelloConsoleMock has been called with "Hello Test!" + if console.Text != messageText { + t.Fatalf("HelloConsole expected text %s but got %s", messageText, console.Text) + } +} +``` + +3. Test output should look like: + +``` +NUnit Adapter 4.2.0.0: Test execution complete + Passed ShouldPrintHelloMessage [180 ms] + Passed ShouldReturnHelloWorld [7 ms] + +Test Run Successful. +Total tests: 2 + Passed: 2 + Total time: 2.7702 Seconds +``` ## Run this project using 🐳 [docker](https://www.docker.com/) -* Execute `./docker-run.sh` -* Once inside the container: - * Test with `go test -test.v` - * Run with `go run .` - * Build with `go build` + +- Execute `./docker-run.sh` +- Once inside the container: + - Test with `go test -test.v` + - Run with `go run .` + - Build with `go build` ## Run this project locally ### Pre-requisites -* Install [Go](https://go.dev/dl/) + +- Install [Go](https://go.dev/dl/) ### Run locally -* Test with `go test -test.v` -* Run with `go run .` -* Build with `go build` + +- Test with `go test -test.v` +- Run with `go run .` +- Build with `go build` ### Create project from scratch -* Execute `go mod init org.hello/main` + +- Execute `go mod init org.hello/main`