Wednesday 23 August 2023

Build and Unit Tests for .NET Apps with GitHub Actions

 Executing unit tests and viewing results in unit tests in a build pipeline is essential to keep high quality in an application deployment via any pipeline system. GitHub actions are the way forward to implement pipelines with repositories in GitHub. Let's explore how to run unit tests and view results in GitHub actions workflow.

Expected outcome

We need a test run report as shown below, showing summary of each project unit test results as well as details of each project test results.


We can run tests for Windows as well and get a Windows test results report  as shown below.



The report should give us any failed test details as whown below.


How to do

Let's look at step by step how to implment a github action workflow to build and run uni tests for a .NET applications, then generate a results report as shown above.

Full code example for the workflow can be found here in GitHub.

Let's look at how to run the  build and unit test steps for Linux first.

Here we allow to build on push as well as with a manual trigger.

Next we need to create a job for Linux. Here we use ubuntu_latest runner. The permission setting is required to enable the test results trx files to be read for reporting.

The next step is to restore NuGet packages and build the projects. Then run unit tests for each test project (*.Tests.csproj). Each test project generates results as a .trx. Full job code is shown below.

  build_and_unittest_linux:
    runs-on: "ubuntu-latest"
    permissions:
      id-token: write
      contents: read
      checks: write
      
    steps:
    - uses: actions/checkout@v3
    - uses: actions/setup-dotnet@v3
      with:
        dotnet-version: '6.0.x'
    
    - name: Install dependencies
      run: dotnet restore

    - name: Build all projects
      run: dotnet build --no-restore

    - name: Run unit tests
      uses: Amadevus/pwsh-script@v2.0.3
      id: dotnet_test
      with:
        script: |
                 dir "*.Tests.csproj" -Recurse | %{dotnet test  $PSItem.FullName --logger ("trx;LogFileName=" + ($PSItem.Name -replace ".csproj", ".trx")) --no-restore --no-build}
                 dir "*.Tests.trx" -Recurse | %{ copy-item -Path $PSItem.FullName };
    - run: echo '${{ steps.dotnet_test.outputs.result }}'

    - name: Create test report
      uses: dorny/test-reporter@v1.6.0
      if: success() || failure()  # run this step even if previous step failed
      with:
        name: Test Results Linux  # Name of the check run which will be created
        path: ./*.Tests.trx  # Path to test results
        reporter: dotnet-trx  # Format of test results


The report is generated using the .trx files by the dorny/test-reporter@v1.6.0 action. Similar to above Linux job we can run same actions in a Windows runner as well as shown below.
  build_and_unittest_windows:
    runs-on: "windows-latest"
    permissions:
      id-token: write
      contents: read
      checks: write
      
    steps:
    - uses: actions/checkout@v3
    - uses: actions/setup-dotnet@v3
      with:
        dotnet-version: '6.0.x'
    
    - name: Install dependencies
      run: dotnet restore

    - name: Build all projects
      run: dotnet build --no-restore

    - name: Run unit tests
      uses: Amadevus/pwsh-script@v2.0.3
      id: dotnet_test
      with:
        script: |
                 dir "*.Tests.csproj" -Recurse | %{dotnet test  $PSItem.FullName --logger ("trx;LogFileName=" + ($PSItem.Name -replace ".csproj", ".trx")) --no-restore --no-build}
                 dir "*.Tests.trx" -Recurse | %{ copy-item -Path $PSItem.FullName };
    - run: echo '${{ steps.dotnet_test.outputs.result }}'

    - name: Create test report
      uses: dorny/test-reporter@v1.6.0
      if: success() || failure()  # run this step even if previous step failed
      with:
        name: Test Results Windows  # Name of the check run which will be created
        path: ./*.Tests.trx  # Path to test results
        reporter: dotnet-trx  # Format of test results

We can see some code duplication in the  above two jobs. In a next post lest look at how to make it refactored to avoid duplication.

No comments:

Popular Posts