Status Code 303 - See Other

サーバサイド、iOS・アンドロイドアプリ、インフラレベルの話まで幅広くやってます。情報の誤りや指摘・意見などは自由にどうぞ。

Visual Studio Community Edition でカバレッジ計測

概要

Visual Studioカバレッジを計測できるかと思ったら、結構大変だったのでメモ。

環境

OS
Windows7 SP1 64bit
IDE
Microsoft Visual Studio Community 2015 ver14.0.23107

プロダクト作成

プロジェクト作成

今回はプロジェクト名をSampleとする。
f:id:kouki_hoshi:20170520051925p:plain
f:id:kouki_hoshi:20170520051931p:plain

プロダクトコード作成

今回はCartクラスを作成する。

using System.Collections.Generic;

namespace Sample
{
	public class Cart
	{
		public List<Item> Items { get; }

		public Cart()
		{
			Items = new List<Item>();
		}

		public int Count
		{
			get { return Items.Count; }
		}

		public Item Get(int index)
		{
			return Items[index];
		}

		public void Add(Item item)
		{
			Items.Add(item);
		}
	}
}

ついでにカートの中に突っ込むItemクラスを作ってみる。

namespace Sample
{
	public class Item
	{
		public int price { get; set;  }

		public string name { get; set; }
	}
}

f:id:kouki_hoshi:20170520052007p:plain

テスト用プロジェクト作成

今回はテスト対象のテストがSampleなので、プロジェクト名を Sample.Test とする。
f:id:kouki_hoshi:20170520052017p:plain
f:id:kouki_hoshi:20170520052024p:plain

テスト用プロジェクトにカバレッジ用のライブラリを入れる
  • OpenCover
  • ReportGenerator

f:id:kouki_hoshi:20170520052049p:plain
f:id:kouki_hoshi:20170520052057p:plain

プロダクト参照をテストプロジェクトに設定

f:id:kouki_hoshi:20170520052306p:plain
f:id:kouki_hoshi:20170520052310p:plain

テストコード作成

今回はCartクラスに対してのみテストするため、名前をCartTestとする。なお、テストフレームワークVisual Studio 標準。

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Sample;

namespace SampleTest
{
	[TestClass]
	public class CartTest
	{

		Cart target;

		[TestInitialize]
		public void カート作成()
		{
			target = new Cart();
		}

		[TestMethod]
		public void 商品は空であること()
		{
			Assert.AreEqual(0, target.Items.Count);
		}

		[TestMethod]
		public void 商品が追加できること()
		{
			Item item = new Item();
			target.Add(item);

			Assert.AreEqual(1, target.Count);
			Assert.AreSame(item, target.Items[0]);
		}

	}
}

f:id:kouki_hoshi:20170520052339p:plain

カバレッジ用のバッチを作成する

OpenCover を使ってコードカバレッジを計測したメモ - present
基本的に上記を参考に作成。しかし、私の環境では、OpenCoverやReportGeneratorが上記と異なるところにある。NuGetで取得したから?
以下のバッチファイルを、今回はテストプロジェクトのルートに置いておく。

また、今回自動でレポートを開くようにするため、以下を参考にした。
参考:バッチファイルからURLをブラウザで開く - Qiita

REM ###### Settings ######

SET PROJECT_NAME=Sample
SET MS_TEST=C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\MSTest.exe

SET REPORT_NAME=result.xml
SET OUTPUT_DIR=.\html

SET OPEN_COVER=.\packages\OpenCover.4.6.519\tools\OpenCover.Console.exe
SET REPORT_GEN=.\packages\ReportGenerator.2.5.8\tools\ReportGenerator.exe

SET TEST=.\%PROJECT_NAME%.Test\bin\Debug\%PROJECT_NAME%.Test.dll
SET COVERAGE_DIR=.\%PROJECT_NAME%\bin\Debug\
SET FILTERS=+[%PROJECT_NAME%]*

REM #######################

call :EXECUTE "%TEST%"
start "" %OUTPUT_DIR%\index.htm

exit

:EXECUTE

%OPEN_COVER% -register:user -target:"%MS_TEST%" -targetargs:"/noisolation /testcontainer:\"%~f1\"" -targetdir:%COVERAGE_DIR% -filter:"%FILTERS%" -output:%REPORT_NAME% -mergebyhash
%REPORT_GEN% "%REPORT_NAME%" %OUTPUT_DIR%

exit /b

設定項目は環境に合わせて変更する。

PROJECT_NAME
プロダクトのプロジェクト名。
MS_TEST
Microsoftの提供するテストアプリケーションの場所。バージョンやOSのビットによって変化するかも。
REPORT_NAME
デフォルトで特に動作に支障なし。レポート(xml)の出力先を指定する。
OUTPUT_DIR
デフォルトで特に動作に支障なし。レポート(htm)の出力先を指定する。
OPEN_COVER
OpenCoverのバージョンによって変わるかも。
TEST
テスト用dllのパス。デフォルト設定のDebugで生成されるパスを指定してある。異なる場所にあるなら変更。
COVERAGE_DIR
カバレッジを計るdllがあるディレクトリパス。デフォルト設定のDebugで生成されるパスを指定してある。異なる場所にあるなら変更。
FILTERS
カバレッジを計測するdllの条件を記載。

f:id:kouki_hoshi:20170520052428p:plain
f:id:kouki_hoshi:20170520052437p:plain
f:id:kouki_hoshi:20170520052439p:plain

なお、Visual Studio で保存すると、なぜかBOM付きで保存されるので、テキストエディタでBOMを取らないと動かない。
上記は、UTF-8 BOMなし形式で動作確認している。

バッチ仕様

このバッチは、XXXがプロダクトのプロジェクト名だとすると、XXX.Testをテストプロジェクトとするように定めている。
参照dllはプロダクト・テストともDebugで出力されるものを使用する。

使い方

プロジェクトのビルド

最終的にdllを見ることになるので、両プロジェクトをビルドしておく。
f:id:kouki_hoshi:20170520052519p:plain

パッケージマネージャー コンソールで以下のバッチコマンドを実行する
PS> .\Sample.Test\coverage.bat

f:id:kouki_hoshi:20170520052540p:plain

以下のようなレポートが表示されれば成功。
f:id:kouki_hoshi:20170520052556p:plain

NUnitの場合

NUnit.Console を NuGetから取得して、
バッチの2行を変更する。

# 4行目
SET MS_TEST=C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\MSTest.exe
# 25行目
%OPEN_COVER% -register:user -target:"%MS_TEST%" -targetargs:"/noisolation /testcontainer:\"%~f1\"" -targetdir:%COVERAGE_DIR% -filter:"%FILTERS%" -output:%REPORT_NAME% -mergebyhash

  ↓

# 4行目
SET NUNIT=.\packages\NUnit.ConsoleRunner.3.6.1\tools\nunit3-console.exe
# 25行目
%OPEN_COVER% -register:user -target:"%NUNIT%" -targetargs:"\"%~f1\"" -targetdir:%COVERAGE_DIR% -filter:"%FILTERS%" -output:%REPORT_NAME% -mergebyhash

これだと Visual Studio のインストール場所が依存しなくなるかも?

課題

事前にビルドしないとソースコードと異なる結果が出て勘違いするかもしれないので、ビルドしてから実行させる方がいい。
現状では、カバレッジに失敗してもエラー処理がないので、カバレッジ情報失敗しても最後まで処理が走ってしまう。

最後に

正直、色々なところで躓いた。まず、文献がそれほどないこと。
Visual Studio Ultimate 以上なら使えるらしいが、そうすると Professional に比べて値段がおよそ3倍以上になること。

OpenCoverをGUIで動作させることができるという拡張プラグイン(OpenCover UI)が動作しなかったこと。
バッチファイルの書き方がよく分からなかったり、Visual Studioでのバッチ起動がよく分からなかったり。
(パッケージマネージャーコンソールからの実行で本当にいいのか?^^;)

などなど。ただ、いい勉強にはなりました!