Quantcast
Channel: C#タグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 9529

自作のWPFアプリを後から自動テスト・DI・CI/CD対応にしてみる。その3

$
0
0
概要 これはもともとあったMVVMなWPFアプリに自動テストとDependency-Injection(DI)とCI/CDを実装してみた記録です。変更点はWPFとはあまり関係ないので、C#であれば他のフレームワークにも参考になるかもしれません。 前回は自動テストとDIの追加までやりました。 今回はGitHubActionsを使ってCI/CDを追加してみます。 コードをpushするだけで、Github上でビルド・テスト実行・テストカバレッジのアップロード・Releaseページの下書き作成までが自動でできます。 WPFアプリの中身はファイルリネーマーです。アプリの詳細はこちらで。コード行数850行ぐらい、クラス数40個ぐらいのサンプルアプリに毛の生えたぐらいのコードサイズです。 TestフレームワークはxUnit、DIコンテナはMicrosoft.Extensions.DependencyInjection、CIはGitHubActionsを使用しました。 GithubActionsの作成 YAMLファイルの追加 まず、GitHubActionsのソースコードにあたる、YAMLファイルを作成します。 GithubのActionsタブにいくと、オススメのWorkflowが表示されているので、".NET Desktop"を選んで追加します。似たのに".NET"がありますが、こちらはデスクトップアプリケーション以外の.NET用です。 デスクトップアプリケーションに適したWorkflowが追加されるので、これを修正していきます。 そのままGithub上で編集してもよいですし、PullしてローカルでVSCode等を使用して編集してもよいです。 環境変数などの追加 アプリケーションプロジェクトやテストプロジェクトの名前などを指定します。 デフォルトで入っているenv:の部分を修正します。 dotnet-desktop.yml env: App_Name: FileRenamerDiff Solution_Directory: src Solution_Path: src/FileRenamerDiff.sln App_Project_Path: src/FileRenamerDiff/FileRenamerDiff.csproj Test_Directory: UnitTests CodeCov_Result: "lcov.xml" Dump表示の追加 これは必須ではありませんが、GithubActionsをデバッグするときに便利なのでDump表示をstepsの最初に入れておくことをオススメします。 dotnet-desktop.yml steps: # Dump for debug workflow - name: Dump Github Context env: GitHub_Context: ${{ toJson(github) }} run: echo "${GitHub_Context}" リストア→ビルド 次にアプリケーションをビルドしますが、その前にリストア(依存関係の解決)します。 ローカルでのビルドやテストでは自動でリストアが走っています。しかしGithubActions上では事前に明示的にリストアして、ビルドやテストではリストア処理をスキップすることでワークフローが効率化できます。 dotnet-desktop.yml # Restore before build and test - name: Restore run: dotnet restore ${{ env.Solution_Path }} - name: Build with dotnet run: dotnet build ${{ env.App_Project_Path }} --no-restore env: Configuration: ${{ matrix.configuration }} テスト実行→カバレッジ計算 そしていよいよテストの実行です。今回はテストの実行と共にテストカバレッジの計算までしています。 いくつかのファイルはカバレッジに含めたくないので、除外パターンを指定しています。基本的にView関係のファイル(XAML・コードビハインド・リソースファイル)は除いています。ただしConverterなどはテストできるので含めます。 dotnet-desktop.yml # Execute all unit tests in the solution - name: Execute unit tests run: > dotnet test ${{ env.Solution_Path }} --verbosity normal --no-restore --collect:"XPlat Code Coverage" /p:CollectCoverage=true /p:CoverletOutputFormat=opencover /p:ExcludeByFile="**.Designer.cs%2c**.xaml*%2c**.g.cs%2c**.xaml" -p:coverletOutput=${{ env.CodeCov_Result }} env: Configuration: ${{ matrix.configuration }} そうして計算したカバレッジ結果をアップロードします。 dotnet-desktop.yml - name: Send coverage result to codecov uses: codecov/codecov-action@v2.0.3 with: files: ${{ env.Solution_Directory }}/${{ env.Test_Directory }}/${{ env.CodeCov_Result }} Release時のみ実行Job ここまではpushされたら必ず実行する内容でしたが、ここからはRelease時にのみ変更します。 まず、実行条件を分けるので、別のjobにします。 コミットにv---というタグがついていた場合は、リリースタグとみなして、jobを実行します。 dotnet-desktop.yml create-release: runs-on: windows-latest needs: [build] if: "contains( github.ref , 'v')" steps: ... Publishの実行 dotnet publishで自己完結型、単一ファイルの指定をして、アプリケーションの発行を行います。x86とx64で2回行います。 発行完了後、ファイルをアップロードしておきます。 dotnet-desktop.yml - name: dotnet publish x86 run: dotnet publish ${{ env.App_Project_Path }} -c Release -r win-x86 --self-contained true -p:PublishTrimmed=false -p:PublishSingleFile=true -p:PublishReadyToRun=true -o outputs\${{ env.app_x86_name }} - name: dotnet publish x64 run: dotnet publish ${{ env.App_Project_Path }} -c Release -r win-x64 --self-contained true -p:PublishTrimmed=false -p:PublishSingleFile=true -p:PublishReadyToRun=true -o outputs\${{ env.app_x64_name }} - name: Archive publish files uses: actions/upload-artifact@v1 with: name: FileRenamerDiff_apps path: outputs Releaseノートの下書き Releaseノートの下書きを作ります。毎回デザイン修正とバグフィックスはあるので、最初から書いておきます。 dotnet-desktop.yml - name: Create release id: create_release uses: actions/create-release@v1 env: GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} with: tag_name: v${{ env.version }} release_name: Ver ${{ env.version }} body: | - Change design - Bug fix draft: true prerelease: false ReleaseノートへのAssetsの追加 さきほど作成したPublishファイルをZipに圧縮してリリースノートのAssetsへ追加します。 dotnet-desktop.yml - name: Archive packages shell: pwsh run: | Compress-Archive -Path outputs\${{ env.app_x86_name }} -DestinationPath ${{ env.app_x86_name }}.zip Compress-Archive -Path outputs\${{ env.app_x64_name }} -DestinationPath ${{ env.app_x64_name }}.zip - name: Upload Release Asset uses: csexton/release-asset-action@v2 with: github-token: ${{ secrets.GITHUB_TOKEN }} release-url: ${{ steps.create_release.outputs.upload_url }} files: | ${{ env.app_x86_name }}.zip ${{ env.app_x64_name }}.zip 全体 YAMLファイル全体は以下です。 dotnet-desktop.yml name: .NET Build and Test on: push: env: App_Name: FileRenamerDiff Solution_Directory: src Solution_Path: src/FileRenamerDiff.sln App_Project_Path: src/FileRenamerDiff/FileRenamerDiff.csproj Test_Directory: UnitTests CodeCov_Result: "lcov.xml" jobs: build: strategy: matrix: configuration: [Debug, Release] runs-on: windows-latest steps: # Dump for debug workflow - name: Dump Github Context env: GitHub_Context: ${{ toJson(github) }} run: echo "${GitHub_Context}" - name: Checkout uses: actions/checkout@v2 with: fetch-depth: 0 # Install the .NET Core workload - name: Install .NET Core uses: actions/setup-dotnet@v1 with: dotnet-version: 5.0.x # Add MsBuild to the PATH: https://github.com/microsoft/setup-msbuild - name: Setup MSBuild.exe uses: microsoft/setup-msbuild@v1.0.2 # Restore before build and test - name: Restore run: dotnet restore ${{ env.Solution_Path }} - name: Build with dotnet run: dotnet build ${{ env.App_Project_Path }} --no-restore env: Configuration: ${{ matrix.configuration }} # Execute all unit tests in the solution - name: Execute unit tests run: > dotnet test ${{ env.Solution_Path }} --verbosity normal --no-restore --collect:"XPlat Code Coverage" /p:CollectCoverage=true /p:CoverletOutputFormat=opencover /p:ExcludeByFile="**.Designer.cs%2c**.xaml*%2c**.g.cs%2c**.xaml" -p:coverletOutput=${{ env.CodeCov_Result }} env: Configuration: ${{ matrix.configuration }} - name: Send coverage result to codecov uses: codecov/codecov-action@v2.0.3 with: files: ${{ env.Solution_Directory }}/${{ env.Test_Directory }}/${{ env.CodeCov_Result }} create-release: runs-on: windows-latest needs: [build] if: "contains( github.ref , 'v')" steps: - name: echos shell: bash run: | echo $RELEASE_VERSION echo version=${GITHUB_REF/refs\/tags\/v/} echo "version=${GITHUB_REF/refs\/tags\/v/}" >> $GITHUB_ENV echo "app_x86_name=${{ env.App_Name }}_app_win-x86_ver${{ env.version }}" >> $GITHUB_ENV echo "app_x64_name=${{ env.App_Name }}_app_win-x64_ver${{ env.version }}" >> $GITHUB_ENV pwd - name: confirm env value shell: bash run: | echo "env.version=${{ env.version }}" echo "app_x86_name=${{ env.app_x86_name }}" echo "app_x64_name=${{ env.app_x64_name }}" - name: Checkout uses: actions/checkout@v2 with: fetch-depth: 0 - name: dotnet publish x86 run: dotnet publish ${{ env.App_Project_Path }} -c Release -r win-x86 --self-contained true -p:PublishTrimmed=false -p:PublishSingleFile=true -p:PublishReadyToRun=true -o outputs\${{ env.app_x86_name }} - name: dotnet publish x64 run: dotnet publish ${{ env.App_Project_Path }} -c Release -r win-x64 --self-contained true -p:PublishTrimmed=false -p:PublishSingleFile=true -p:PublishReadyToRun=true -o outputs\${{ env.app_x64_name }} - name: Archive publish files uses: actions/upload-artifact@v1 with: name: FileRenamerDiff_apps path: outputs - name: Create release id: create_release uses: actions/create-release@v1 env: GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} with: tag_name: v${{ env.version }} release_name: Ver ${{ env.version }} body: | - Change design - Bug fix draft: true prerelease: false - name: Archive packages shell: pwsh run: | Compress-Archive -Path outputs\${{ env.app_x86_name }} -DestinationPath ${{ env.app_x86_name }}.zip Compress-Archive -Path outputs\${{ env.app_x64_name }} -DestinationPath ${{ env.app_x64_name }}.zip - name: Upload Release Asset uses: csexton/release-asset-action@v2 with: github-token: ${{ secrets.GITHUB_TOKEN }} release-url: ${{ steps.create_release.outputs.upload_url }} files: | ${{ env.app_x86_name }}.zip ${{ env.app_x64_name }}.zip 実行結果 GithubActions進行状況 YAMLファイルを追加すると、GithubActionsが実行されます。 GitHubのActionsタブにいくと、進行状況が見れます。 buildjobはconfigurationをDebugとReleaseの2つ指定したので、並列で2つのjobが走ります。それらが終わって、コミットタグにv~が指定されていたら、create-releasejobが走ります。 完了すると、緑色の表示になります。 Releaseノート Releaseノートにいくと、下書きができていますので、リリース内容を確認してPublishします。 テストカバレッジ表示 テストカバレッジの結果をアップロードしているので、カバレッジが経時的に変化しているかも見ることができます。 こうやってVisualizeされると退屈なテストを書くモチベーションも上がりますね! デバッグのコツ うまくワークフローが動くようになるとGithubActionsはとても便利ですが、最初はつまずくこともあります。 ここでは私が行ったいくつかの工夫を紹介します。 GithubActions練習用レポジトリの作成 GithubActionsの練習をアプリケーションのレポジトリでやってしまうとコミットツリーにノイズが増えすぎてしまいます。 またアプリケーションの規模大きくなると、ワークフローの実行時間も増えてきてしまいます。 最終的には本番レポジトリでも試行錯誤が必要ですが、GithubActions自体の練習は専用のレポジトリを作成したほうがよいでしょう。 Dumpを確認する GithubActionsがうまく動かない原因は、だいたいレポジトリ名やファイルパスなどが間違っていることです。 Run結果の一番最初のDump Github Contextの中を確認して他の変数指定とあっているかを確認します。 ログを見やすく GithubActionsのログは"個別のJobのRun結果>⚙アイコン>View raw logs"から表示できます。 ただしそのままではハイライトも無く見づらいのでダウンロードして.logファイルとしてVSCodeで見ると色がついて見やすくなります。 Re-run ローカルでは成功するテストがGithubActions上では失敗することがあります。この場合、ワークフローを再実行することで成功することもあります。 "個別のJobのRun結果>Re-run all jobs"で再実行します。 ただ、そもそも環境によって結果が変わるテストは筋が悪いので、テストコードを修正したほうがよいでしょう。 注意点 プライベートレポジトリの場合はいくつかのstepで認証が必要になるかもしれません。そのあたりはGithubActionsのドキュメントか使用するActionのレポジトリを確認してください。 後書き GitHubActionsは動作を確認するためにCommitが必要になるので、試行錯誤を繰り返すと自分のGithubマイページの芝がよく生えます。 なれると自動でGithub側が色々やってくれてスゴイ便利です。 参考 環境 VisualStudio 2019 C# 9 .NET 5 Microsoft.NET.Test.Sdk 16.9.4 xunit 2.4.1 xunit.runner.visualstudio 2.4.3 coverlet.collector 3.0.2 System.IO.Abstractions 13.2.38 System.IO.Abstractions.TestingHelpers 13.2.38

Viewing all articles
Browse latest Browse all 9529

Trending Articles