Part Cover 紹介 その1 - Part Cover browser から テストコード(NUnit)のカバレッジを取得する

.NET のカバレッジツールについて紹介します。
Visual Studio でもテストコードのカバレッジが測定出来るようですが、Team Edition じゃないとダメなのかな?
チュートリアル : テストを実行し、コード カバレッジを表示する
また、TestDriven.NET TestDriven.Net > Home というツールもありますが、これも商用利用はライセンス料が掛かるみたいです。*1
というわけで、PartCover PartCover | Free Development software downloads at SourceForge.net を紹介しようと思います。
※現在のところ PartCover は 64bit 環境では動作しない様なので気を付けてください!


タイトルに「その1」とついているのは、何回かに分けて行う予定です。
Part Cover 紹介 その2 - Part Cover browser から Windows アプリケーションのカバレッジを取得する
Part Cover 紹介 その3 - Part Cover console から テストコード(NUnit)のカバレッジを取得する
Part Cover 紹介 その4 - ビルドに組み込む。MSBuild から Part Cover console を実行する
これとは別に以前
Hudson で PartCover を使ってみた - お だ のスペース
PartCover を CCNETに組み込む | お だ のスペース
も書いてますので、CIサーバーで確認したい場合に参考にどうぞ。


また今回 UnitTestツールは、NUnit NUnit - Home を使用します。
※PartCover は 2.3.0.30159、NUnit は 2.5.4.10098 ともに現時点での最新バージョンをインストールしました。
NUnit だけ動かす場合は、私は普段 NUnitGUI を立ち上げっぱなしにして実行していますが、Visual NUnit というツールもあり、Visual Studio 上で NUnit を実行出来るようです。
※VS2010 からは、Extension Manager からインストール可能です。


今回は、Part Cover browser から NUnit-Console を実行し、ユニットテストカバレッジを取得する手順を説明します。
Part Cover browser を起動します。

次に、メニュー -> File -> Run Target をクリックします。

Executable File には、実行するアプリのパスを指定します。今回は、nunit-console.exe を指定します。*2
Working Directory には、テスト対象のアセンブリがあるディレクトリを指定します。
Working Arguments には、テスト対象のアセンブリを指定します。
Rules には、カバレッジを測る対象を指定します。今回は、+[ネームスペース]計測するクラスのフル名*3 を指定します。

ここで、Save ボタンを押して今回入力した内容を設定ファイルとして保存します。
※設定ファイルを保存した理由は、PartCover を起動し直した際の入力の簡易化と、次回以降で解説する PartCover Console に渡すパラメータを簡略化するためです。
Start ボタンを押すと実行します。NUnit-Console が動きだし、

PartCover の結果が表示されます。

ホントは右側にコードが表示されるんですが…。なんか設定ミスったかな?


設定した項目について、簡単に解説します。
Executable File : 実行するアプリケーションです。NUnit-Console だったり、アプリケーション自身だったりします。
Working Directory: Executable File で指定したアプリケーションが動くカレントディレクトリを指定します。
Working Arguments: Executable File で指定したアプリケーションに渡すパラメータを指定します。
Rules: カバレッジを測る対象を決めます。基本は、[] で指定します。"*" を使ってワイルドカード指定も可能です。また、先頭に "+" を付けるとカバレッジを測る対象に含めます。(include)"-" を付けるとカバレッジを測る対象から外します。(exclude)


今回使用したコードです。Windowsアプリなのですが、フォームにはテキストボックスを3つとボタンを1つ配置しているだけです。

WindowsFormsApplication1.Form1.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
  public partial class Form1 : Form
  {
    public Form1()
    {
      InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
      CalcModel c = new CalcModel();
      c.Arg1 = this.textBox1.Text.ParseInt();
      c.Arg2 = this.textBox2.Text.ParseInt();
      c.Add();
      this.textBox3.Text = c.Result.ToString();
    }
  }
  public static class StringExtension
  {
    public static int? ParseInt(this String source)
    {
      int tmp;
      if (int.TryParse(source, out tmp))
      {
        return tmp;
      }
      return null;
    }
  }
}

WindowsFormsApplication1.CalcModel.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;

namespace WindowsFormsApplication1
{
  public class CalcModel
  {
    public int? Arg1 { get; set; }
    public int? Arg2 { get; set; }
    public int Result { get; private set; }
    public void Add()
    {
      if (!Arg1.HasValue)
      {
        return;
      }
      if (!Arg2.HasValue)
      {
        return;
      }
      Result = Arg1.Value + Arg2.Value;
    }
  }
}

TestProject.CalcModelTest.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using WindowsFormsApplication1;

namespace TestProject
{
  [TestFixture]
  public class CalcModelTest
  {
    CalcModel instance;
    [SetUp]
    public void SetUp()
    {
      this.instance = new CalcModel();
    }
    [Test]
    public void AddArg1がNull()
    {
      this.instance.Arg1 = null;
      this.instance.Arg2 = 1;
      this.instance.Add();
      Assert.AreEqual(0, this.instance.Result);
    }
    [Test]
    public void AddArg2がNull()
    {
      this.instance.Arg1 = 1;
      this.instance.Arg2 = null;
      this.instance.Add();
      Assert.AreEqual(0, this.instance.Result);
    }
    [Test]
    public void Ad()
    {
      this.instance.Arg1 = 1;
      this.instance.Arg2 = 2;
      this.instance.Add();
      Assert.AreEqual(3, this.instance.Result);
    }
  }
}

*1:個人的な利用では、無料で使える様なので商用じゃない場合は使ってみると良いかも

*2:UnitTest では無く、アプリ自体を実行して結果のカバレッジを測ることも出来ます。次回以降紹介します。

*3:ネームスペース.クラス名。例えば String クラスなら、System.String