Tests Everywhere - .NET C#
Tags: test, csharp, nunit, moq
Published: 2023-11-22 23:51:51

{% github rogervinas/tests-everywhere no-readme %}

[.NET C#](https://learn.microsoft.com/dotnet/csharp) testing [this simple Hello World](https://dev.to/rogervinas/tests-tests-everywhere-1b6h) 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](https://github.com/rogervinas/tests-everywhere/tree/main/dotnet#run-this-project-using--docker)
- [Run this project locally](https://github.com/rogervinas/tests-everywhere/tree/main/dotnet#run-this-project-locally)

## Show me the code

### Implementation

1. Create `HelloMessage` interface and `HelloWorldMessage` implementing it in [HelloMessage.cs](https://github.com/rogervinas/tests-everywhere/blob/main/dotnet/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 [HelloConsole.cs](https://github.com/rogervinas/tests-everywhere/blob/main/dotnet/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 [HelloApp.cs](https://github.com/rogervinas/tests-everywhere/blob/main/dotnet/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](https://github.com/rogervinas/tests-everywhere/blob/main/dotnet/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) and [Moq](https://github.com/devlooped/moq#moq) guides ...

1. Test `HelloMessage` in [HelloMessageTest.cs](https://github.com/rogervinas/tests-everywhere/blob/main/dotnet/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 [HelloAppTest.cs](https://github.com/rogervinas/tests-everywhere/blob/main/dotnet/Hello.Test/HelloAppTest.cs):

```csharp
[Test]
public void ShouldPrintHelloMessage()
{
  var messageText = "Hello Test!";

  // 2.1 Create a mock of HelloMessage
  var messageMock = new Mock<HelloMessage>();
  // 2.2 Expect HelloMessage mock to receive a call to Text
  // and return "Hello Test!"
  messageMock.Setup(message => message.Text).Returns(messageText);
  // 2.3 Get the mock object to pass it to HelloApp
  var message = messageMock.Object;

  // 2.4 Create a mock of HelloConsole
  var consoleMock = new Mock<HelloConsole>();
  // 2.5 No need to set expectations for this one
  // 2.6 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);
  // 2.4 Execute the method we want to test
  app.PrintHello();

  // 2.5 Verify HelloConsole mock Print() method
  // has been called once 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
```

Happy Testing! 💙