マルチフレームワーク対応とユニットテストの実行 - Magicodes.IEの実践

背景

多くの場合、ツールライブラリを開発した後、特定のフレームワークバージョンで問題が発生することがあります。例えば、最近私が開発したインポート/エクスポートツールライブラリMagicodes.IE(GitHub:https://github.com/xin-lai/Magicodes.IE)では以下の問題が発生しました:

標準ライブラリ2.0をサポートしているにもかかわらず、ユニットテストの実行により以下の問題が明らかになりました:

したがって、ユニットテストは各フレームワークバージョンに対して実行する必要があり、関連するコードはフレームワークに合わせて互換性の修正が必要です。では、どのように実装するのでしょうか?以下で段階的に実践していきます。

.NETフレームワークバージョンの説明

最新のターゲットフレームワークバージョン

下表は、最も一般的なターゲットフレームワーク、それらの参照方法、および実装されている.NET Standardバージョンを定義しています。これらのターゲットフレームワークバージョンは最新の安定版です。プレビュー版は表示されません。ターゲットフレームワーク名オブジェクト(TFM)は、.NETアプリまたはライブラリのターゲットフレームワークを指定するための標準化されたトークン形式です。

ターゲットフレームワーク 最新安定版 ターゲットフレームワーク名オブジェクト(TFM) 実装.NET Standardバージョン
.NET Standard 2.1 netstandard2.1 不適用
.NET Core 3.0 netcoreapp3.0 2.1
.NET Framework 4.8 net48 2.0

サポートされるターゲットフレームワークバージョン

ターゲットフレームワークは通常TFMで参照されます。下表は.NET Core SDKおよびNuGetクライアントがサポートするターゲットフレームワークを示しています。同等のものは括弧内に表示されます。例えば、win81netcore451に対するTFMとして同等です。

ターゲットフレームワーク TFM
.NET Standard netstandard1.0 netstandard1.1 netstandard1.2 netstandard1.3 netstandard1.4 netstandard1.5 netstandard1.6 netstandard2.0 netstandard2.1
.NET Core netcoreapp1.0 netcoreapp1.1 netcoreapp2.0 netcoreapp2.1 netcoreapp2.2 netcoreapp3.0
.NET Framework net11 net20 net35 net40 net403 net45 net451 net452 net46 net461 net462 net47 net471 net472 net48
Windows ストアアプリ netcore [netcore45] netcore45 [win] [win8] netcore451 [win81]
.NET Micro Framework netmf
Silverlight sl4 sl5
Windows Phone wp [wp7] wp7 wp75 wp8 wp81 wpa81
ユニバーサルWindowsプラットフォーム uap [uap10.0] uap10.0 [win10] [netcore50]

プロジェクトが複数のフレームワークをサポートする方法

ユニットテストプロジェクトを例に取ります:

<PropertyGroup>
    <TargetFrameworks>netcoreapp3.0;netcoreapp2.2;netcoreapp3.1;net461</TargetFrameworks>
    <IsPackable>false</IsPackable>
  </PropertyGroup>

上記のコードのように、「TargetFrameworks」要素を使用して複数のフレームワークを定義できます。では、プロジェクト内でどのように条件判断を追加してコンパイルするのでしょうか?

<ItemGroup Condition=" '$(TargetFramework)' == 'net461' ">
    <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
  </ItemGroup>

プロジェクト内だけでなく、コード内でどのように異なるフレームワークバージョンに対応したコードを記述するのでしょうか?

#if NET461
            return excelPackage.Workbook.Worksheets[typeof(T).GetDisplayName()] ??
                   excelPackage.Workbook.Worksheets[ExcelImporterSettings.SheetName] ??
                   excelPackage.Workbook.Worksheets[1];
#else
            return excelPackage.Workbook.Worksheets[typeof(T).GetDisplayName()] ??
                   excelPackage.Workbook.Worksheets[ExcelImporterSettings.SheetName] ??
                   excelPackage.Workbook.Worksheets[0];
#endif

これらの「NET461」のようなシンボルにはどのようなものがあるのでしょうか?以下に示します:

完全な.NET Coreターゲットフレームワークのプリプロセッサシンボルリスト

ターゲットフレームワーク シンボル
.NET Framework NETFRAMEWORK, NET20, NET35, NET40, NET45, NET451, NET452, NET46, NET461, NET462, NET47, NET471, NET472, NET48
.NET Standard NETSTANDARD, NETSTANDARD1\_0, NETSTANDARD1\_1, NETSTANDARD1\_2, NETSTANDARD1\_3, NETSTANDARD1\_4, NETSTANDARD1\_5, NETSTANDARD1\_6, NETSTANDARD2\_0, NETSTANDARD2\_1
.NET Core NETCOREAPP, NETCOREAPP1\_0, NETCOREAPP1\_1, NETCOREAPP2\_0, NETCOREAPP2\_1, NETCOREAPP2\_2, NETCOREAPP3\_0, NETCOREAPP3\_1

Magicodes.IEのマルチフレームワークバージョンテスト

これらの知識を理解すれば、マルチフレームワークバージョンの実装とユニットテストを記述できます。

ユニットテストにマルチフレームワークサポートを追加

  1. TargetFrameworkをTargetFrameworksに変更する

  2. グループ設定

ターゲットフレームワークに基づいてグループ化します:

  1. コンパイルエラーの修正
  2. ユニットテストの実行

ユニットテストの結果は以下の図のようになります:

具体的なユニットテストについては、ターゲットフレームワークを指定して実行およびデバッグすることもできます:

経験とベストプラクティスの共有

改善プロセスを総合すると、以下の経験を得られます:

  1. ユニットテストの例外メッセージ判断では、完全な形式を判断するのではなく、文字列包含判断を使用することを推奨します。

上記のように、net461、netcoreapp2.2では左側のコードは通過できません。フォーマットされたパラメータテンプレートが一貫していません。

  1. 特定のフレームワーク、プラットフォームのコードにはシンボル判断を追加する

以下の例のように、デフォルトでは.NET Coreはコードページ28591以外の他のコードページエンコーディングやUnicodeエンコーディング(UTF-8やUTF-16など)を提供しないため、以下のコードを使用して追加できますが、.NET Frameworkを除外する必要があります:

特定のコード以外にも、特定の依存パッケージが必要な場合があります:

  1. 新しい構文糖を慎重に使用する

以下のコードのような場合:

リソースに限りがあり、あまり多くのフレームワークバージョンに対応できませんが、興味のある方は参加してください。

タグ: .NET .NET Core .NET Framework ユニットテスト マルチフレームワーク

5月29日 10:13 投稿