.NET でテストプロジェクトを作成して自動テストを行う

# 概要
Microsoftのチュートリアルを参考に.NETのプロジェクトの作成からGithubActionsを利用したCIを構築する

# ハンズオンの内容

- .NETでクラスライブライプロジェクトの作成
- .NETでテストプロジェクトの作成
- Github に Repository を作成
- GithubActions による CI の作成

# 環境

- VsCode
- .NET 8 SDK

# .NET クラス ライブラリを作成する

- ソリューションの作成

```
dotnet new sln
```

- クラスライブラリのプロジェクトを作成

```
dotnet new classlib -o StringLibrary
```

- ライブラリをソリューションに追加

```
dotnet sln add StringLibrary/StringLibrary.csproj
```

- Class1.cs を開きサンプルソースに置き換える

```cs
namespace UtilityLibraries;

public static class StringLibrary
{
  public static bool StartsWithNumber(this string? str)
  {
    if (string.IsNullOrWhiteSpace(str))
      return false;

    char ch = str[0];
    return char.IsDigit(ch);
  }
}
```

- ビルドが成功することを確認する

```
dotnet build
```

# テストプロジェクトを作成する

- テストプロジェクトを作成する

```
dotnet new mstest -o StringLibraryTest
```

- ソリューションにテストプロジェクトを追加する

```
dotnet sln add StringLibraryTest/StringLibraryTest.csproj
```

- テストプロジェクトにクラスライブラリのプロジェクトへの参照を追加する

```
dotnet add StringLibraryTest/StringLibraryTest.csproj reference StringLibrary/StringLibrary.csproj
```

- 単体テストの追加
  StringLibraryTest/UnitTest1.cs を開き単体テストコードを追加する
  
```cs
using Microsoft.VisualStudio.TestTools.UnitTesting;
using UtilityLibraries;

namespace StringLibraryTest;

[TestClass]
public class UnitTest1
{
    [TestMethod]
    public void TestStartsWithDigit()
    {
        // Tests that we expect to return true.
        string[] words = { "1hoge", "5huga" };
        foreach (var word in words)
        {
            bool result = word.StartsWithNumber();
            Assert.IsTrue(result,
                   string.Format("Expected for '{0}': true; Actual: {1}",
                                 word, result));
        }
    }

    [TestMethod]
    public void TestDoseNotStartsWithDigit()
    {
        // Tests that we expect to return true.
        string[] words = { "h1oge", "huga2" };
        foreach (var word in words)
        {
            bool result = word.StartsWithNumber();
            Assert.IsFalse(result,
                   string.Format("Expected for '{0}': true; Actual: {1}",
                                 word, result));
        }
    }
}
  ```

- テストを実行する

```
dotnet test StringLibraryTest/StringLibraryTest.csproj
```

# Github にリポジトリを用意する
- Github上からリポジトリを用意する
- リポジトリを作成しリモートへプッシュする

```
git init
git add README.md
git commit -m "first commit"
git branch -M main
git remote add origin git@github.com:koakirado/dotnet-ci-sample.git
git push -u origin main
```

- .gitignoreを作成する
```
dotnet new gitignore
```
# GithubActions にワークフローを用意する
- Actionsタブを開き"set up a workflow yourself "からyamlファイルを作成する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/658524/259e4001-cacf-2726-3e2b-ce6744491091.png)
- yamlファイルを以下のソースに置き換える
```yaml
name: .NET Core CI

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - name: Checkout code
      uses: actions/checkout@v4

    - name: Setup .NET Core
      uses: actions/setup-dotnet@v4
      with:
        dotnet-version: 8.0.x

    - name: Restore dependencies
      run: dotnet restore

    - name: Build
      run: dotnet build --no-restore --configuration Release

    - name: Run tests
      run: dotnet test --no-build --verbosity normal --configuration Release
```
- プッシュによりワークフローが実行されることを確認する

# 参考

- チュートリアル: Visual Studio Code を使用して .NET クラス ライブラリを作成する
  https://learn.microsoft.com/ja-jp/dotnet/core/tutorials/library-with-visual-studio-code?pivots=dotnet-8-0
- チュートリアル: Visual Studio Code を使用して .NET クラス ライブラリをテストする
  https://learn.microsoft.com/ja-jp/dotnet/core/tutorials/testing-library-with-visual-studio-code?pivots=dotnet-8-0
- dotnetコマンドで.gitignoreを作成する
https://tera1707.com/entry/2023/02/01/214704
- GitHub Actions のクイックスタート
https://docs.github.com/ja/actions/writing-workflows/quickstart