20201026のC#に関する記事は9件です。

C# - よく使う機能の逆引きリファレンスを目指して

例によって完成はしてない、が、使い物にはなるはず。記事を育てていきます。

ファイル関連

ファイルの読み書き

やりたいこと 使うクラス名orメソッド 記事or外部リンク 補足・注意事項
指定したテキストファイルを開く(簡易版) File.ReadAllLines dobon.net 文字コードに注意
指定したバイナリファイルを開く(簡易版) File.ReadAllBytes dobon.net

ファイル名・パスを選ぶ

やりたいこと 使うクラス名orメソッド 記事or外部リンク 補足・注意事項
(読み込む対象の)ファイルを選択するダイアログを開く OpenFileDialog dobon.net [STAThread]Mainメソッドの真上の行に入れること!
(保存先の)ファイルを選択するダイアログを開く SaveFileDialog dobon.net [STAThread]Mainメソッドの真上の行に入れること!
フォルダを選択するダイアログを開く FolderBrowserDialog dobon.net [STAThread]Mainメソッドの真上の行に入れること!
Drag&Dropでファイルを指定する(ファイルのパスを受け取る) - dobon.net
指定フォルダからファイルを検索する Directory.GetFiles dobon.net サブフォルダ以下を全検索するかどうかはSearchOptionで指定する

ファイル名・パスに対する操作

Pathクラスで色々やれる。

やりたいこと 使うクラス名orメソッド 記事or外部リンク 補足・注意事項
フルパスを取得する Path.GetFullPath
親となるフォルダのパスを取得する Path.GetDirectoryName 振る舞いを本表の直下に記載。詳細は左記リンク先のサンプルを参照
ファイル名(拡張子を含む)を取り出す Path.GetFileName
ファイル名(拡張子を除く)を取り出す Path.GetFileNameWithoutExtension
拡張子だけ取り出す Path.GetExtension 結果はドット"."を含む。
拡張子がない場合は空文字("")が結果となる。
フォルダ名を連結する Path.Combine

以下、GetDirectoryNameの振る舞い。

Path.GetDirectoryName(@"C:\MyDir\MySubDir\myfile.ext"); // returns 'C:\MyDir\MySubDir'
Path.GetDirectoryName(@"C:\MyDir\MySubDir");            // returns 'C:\MyDir'
Path.GetDirectoryName(@"C:\MyDir\");   // returns 'C:\MyDir'
Path.GetDirectoryName(@"C:\MyDir");    // returns 'C:\'
Path.GetDirectoryName(@"C:\"); // returns ''

ファイルの有無や属性を調べる

やりたいこと 使うクラス名orメソッド 記事or外部リンク 補足・注意事項
ファイルが存在するか調べる File.Exists FileInfoクラスを使う方法もある
ファイルの属性を調べる File.GetAttributes
ファイルの最終更新日時を調べる File.GetLastWriteTime
ファイルのサイズを調べる FileInfo.Length Samurai Blog

文字列関連

String型の重要な特性について ⇒ String型におけるよくある勘違い - dobon.net

Stringクラスのプロパティやメソッドには、データの内容を変更できるものがありません。その代わりに、新しいデータが作成されます。

数値と文字列の間の変換

参考:【C#入門】文字列を数値に、数値を文字列に変換する方法 | 侍エンジニア塾ブログ(Samurai Blog) - プログラミング入門者向けサイト
など

やりたいこと 使うクラス名orメソッド 記事or外部リンク 補足・注意事項
数値を文字列に変換する ToStringメソッド
文字列を数値に変換する Convert.ToInt32
Convert.ToInt64など
パディングする(桁数をそろえる) String.PadLeft 数値変換専用ではないが、数値変換と合わせて使うケースが多そうなのでここに記載してみた。

文字列を調べる(一致性の判定)

やりたいこと 使うクラス名orメソッドor... 記事or外部リンク 補足・注意事項
文字数を調べる String.Length 1
特定の文字列と同一内容の文字列かを調べる 文字列 == 文字列 2
なお、中身の完全一致をチェックするので、大文字小文字を区別します。
特定の文字列で始まっているかを調べる String.StartsWith 大文字小文字を区別します
特定の文字列で終わっているかを調べる String.EndsWith 大文字小文字を区別します
特定の文字列を含んでいるかを調べる String.Contains 大文字小文字を区別します
特定のパターン(正規表現)にマッチしているかを調べる RegexMatch 記事を起したい・・・

文字列処理

やりたいこと 使うクラス名orメソッド 記事or外部リンク 補足・注意事項
前後(先頭と末尾)の空白を削除する String.Trim
先頭の空白を削除する String.TrimStart
末尾の空白を削除する String.TrimEnd
小文字に変換する String.ToLower 3
大文字に変換する String.ToUpper 3
文字列を指定の文字で分割する String.Split
文字列の配列を指定の文字で結合する String.Join
文字列の指定文字目から指定文字数分の文字列を取り出す String.Substring
特定のパターン(正規表現)にマッチした部分を取り出す RegexMatch 記事を起したい・・・

日時情報の変換

画像関連

画像ファイルの表示

画像の加工

画像の保存

所感

Microsoft Docsとdobon.netさまの記事リンク集と化してしまった感が否めない。。否みようがない。。

参考サイト


  1. サロゲートペアとか結合文字とか、見た目1文字だけど内部表現が2文字以上になるやつが世の中には居るので注意。 

  2. String型は参照型の中では特殊な扱いになっていて、C#においてはString同士を==を使って比較すると中身の比較がなされる。 

  3. String.ToUpper/ToUpperInvariantとToLower/ToLowerInvariantメソッドで異なる結果となる例 (C#) - .NET サンプルコード - 総武ソフトウェア推進所 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C# - TextBoxのサンプル コードべた書き(Visual Studio不使用)

目次: C# - Windows Formsでよく使うコントロールたち (Visual Studioなし環境向け) - Qiita

単一行テキストボックス

単一行テキストボックスの画面キャプチャ

image.png

単一行テキストボックスのテンプレ(サンプルコード)

using System;
using System.Drawing;
using System.Windows.Forms;

class TextBoxSample : Form
{
    TextBox txt;

    TextBoxSample()
    {
        ClientSize = new Size(300, 100);

        Controls.Add(txt = new TextBox(){
            Location = new Point(20, 30),
            Width = 250,
        });

        txt.TextChanged += Txt_TextChanged;
    }

    void Txt_TextChanged(object sender, EventArgs e)
    {
        Text = txt.Text;
    }

    [STAThread]
    static void Main()
    {
        Application.Run(new TextBoxSample());
    }
}

複数行テキストボックス

複数行テキストボックスの画面キャプチャ

image.png

複数行テキストボックスのテンプレ(サンプルコード)

using System;
using System.Drawing;
using System.Windows.Forms;

class TextBoxSample : Form
{
    TextBox txt;

    TextBoxSample()
    {
        ClientSize = new Size(300, 250);

        Controls.Add(txt = new TextBox(){
            Location = new Point(0, 0),
            Size = new Size(300, 250),
            Multiline = true,
            WordWrap = false, // 折り返し表示をしない
            ScrollBars = ScrollBars.Both,
        });
    }

    [STAThread]
    static void Main()
    {
        Application.Run(new TextBoxSample());
    }
}

参考サイト

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C# - NumericUpDownのサンプル コードべた書き(Visual Studio不使用)

目次: C# - Windows Formsでよく使うコントロールたち (Visual Studioなし環境向け) - Qiita

画面キャプチャ

image.png

テンプレ

using System;
using System.Drawing;
using System.Windows.Forms;

class NumericUpDownSample : Form
{
    NumericUpDown nud;

    NumericUpDownSample()
    {
        ClientSize = new Size(300, 100);

        Controls.Add(nud = new NumericUpDown(){
            Location = new Point(0, 0),
            Width = 80,
            Maximum = 20,
            Minimum = 0,
            Value = 5,
        });

        nud.ValueChanged += Nud_ValueChanged;
    }

    void Nud_ValueChanged(object sender, EventArgs e)
    {
        int n = (int)nud.Value; // NumericUpDownのValueプロパティはdecimal型なので、整数にしたい場合は、キャストが必要

        int prod = 1;
        // checked {}で囲むと、オーバーフローを検出したときにエラーを発生させる。
        // ※13の階乗(13!)は C#の intの最大値 (2^31)-1 を超えてオーバーフローする
        checked {
            for ( int k=1; k<=n; k++ ) {
                prod *= k;
            }
        }

        Text = n.ToString()+"! = " + prod.ToString();
    }

    [STAThread]
    static void Main()
    {
        Application.Run(new NumericUpDownSample());
    }
}

ついでにオーバーフロー仕込んでみた・・・1

参考サイト


  1. decimal型のまま計算すればもうちょい行けるけど、論点がずれすぎるのと、精度とかややこしくなりそうなのでやめておく 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C# - ComboBoxのサンプル コードべた書き(Visual Studio不使用)

目次: C# - Windows Formsでよく使うコントロールたち (Visual Studioなし環境向け) - Qiita

画面キャプチャ

image.png

テンプレ

using System;
using System.Drawing;
using System.Windows.Forms;

class ComboBoxSample : Form
{
    ComboBox cmb;

    ComboBoxSample()
    {
        ClientSize = new Size(300, 100);

        Controls.Add(cmb = new ComboBox(){
            Location = new Point(0, 0),
            Width = 80,
            DropDownStyle = ComboBoxStyle.DropDownList, // 直接編集不可設定
            FormattingEnabled = true, // 頂いたコメントを受けて追加しました!
        });
        cmb.Items.AddRange(new string[]{"未選択","出勤","直行","不帰","不在"});
        cmb.SelectedIndex = 0;

        cmb.TextChanged += Cmb_TextChanged;
    }

    void Cmb_TextChanged(object sender, EventArgs e)
    {
        Text = cmb.Text;
    }

    [STAThread]
    static void Main()
    {
        Application.Run(new ComboBoxSample());
    }
}

参考サイト

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C# - ListViewのサンプル コードべた書き(Visual Studio不使用)

目次: C# - Windows Formsでよく使うコントロールたち (Visual Studioなし環境向け) - Qiita

画面キャプチャ

image.png

テンプレ

using System;
using System.Drawing;
using System.Windows.Forms;

class ListViewSample : Form
{
    ListView lsv;

    ListViewSample()
    {
        ClientSize = new Size(500, 300);

        Controls.Add(lsv = new ListView() {
            Dock = DockStyle.Fill,
            View = View.Details,
            FullRowSelect = true,
            HideSelection = false,
            MultiSelect = false,
            GridLines = true,
        });

        // 列ヘッダを登録
        lsv.Columns.Add("angle", 70, HorizontalAlignment.Left);
        lsv.Columns.Add("cos(angle)", 70, HorizontalAlignment.Left);
        lsv.Columns.Add("sin(angle)", 70, HorizontalAlignment.Left);

        lsv.SelectedIndexChanged += Lsv_SelectedItemChanged;

        AddItemsToTheListView();
    }

    void Lsv_SelectedItemChanged(object sender, EventArgs e)
    {
        if ( lsv.SelectedItems.Count == 1 ) { // 無選択(0個)になる場合がありえるため、チェックしている。
            // Form のタイトルを変更
            Text = "angle="+lsv.SelectedItems[0].SubItems[0].Text + ", cos(angle)=" + lsv.SelectedItems[0].SubItems[1].Text;
        }
        else {
            Text = "No item is selected.";
        }
    }

    void AddItemsToTheListView()
    {
        lsv.Items.Clear(); // ※Itemsを忘れると列ヘッダごと消えます

        lsv.BeginUpdate();

        for ( int i=0; i<100; i++ ) {
            double degree = i*7.5;
            double x = degree*Math.PI/180.0;
            double cosX = Math.Cos(x);
            double sinX = Math.Sin(x);
            lsv.Items.Add(new ListViewItem(new string[]{degree.ToString("F1"),cosX.ToString("F3"),sinX.ToString("F3")}));
        }

        lsv.EndUpdate();
    }


    [STAThread]
    static void Main()
    {
        Application.Run(new ListViewSample());
    }
}

参考サイト

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

C# - Windows Formsでよく使うコントロールたち (Visual Studioなし環境向け)(作成中)

よく使うコントロール

クラス名 画面キャプチャ ユーザインタフェース機能概要 補足 My テンプレ
TextBox image.png 文字列を表示・入力する 設定(Multilineプロパティ)により、単一行 or 複数行に対応可能。 記事
ComboBox image.png 選択子から文字列を選んで表示・入力する ユーザーによる任意テキストの入力可 or 不可を設定可能。 記事
NumericUpDown image.png 数値を表示・入力する あらかじめ設定した範囲内の数値の入力を、直接 or 上下ボタンで行える。10進小数も扱える。Valueの型が扱いづらいかもしれない。 記事
Button ボタンを表示する とりあえず何か試したいときは大体これにClickイベントを登録して使う。
MenuStrip 右記記事参照 メニューを表示する Buttonをダサく感じたり、込み入ってきたらコレ。 記事
PictureBox 画像を表示する 画像を表示させたり、キャンバスとして使える。
ListView image.png リストを表示する Windowsのファイラであるエクスプローラのファイル一覧表示みたいなアレ。自分はいつもView = View.Detailsで使っており、行指向で表形式で表示するのに向いている。 記事
TreeView 木構造を表示する 最初に手を出すにはハードル高め。
DataGridView 表形式で表示・入力する エクセルの表をイメージするとイメージ湧きやすいかも。クセ強めの印象。使いこなすには慣れが要りそう。
SplitContainer 右記記事参照 表示領域を分割してユーザがリサイズ容易にする 画面が手狭になってきたら使ったりする。右記記事みたほうが話は早い。 記事 
TabPage image.png タブ表示 右記記事みたほうが話は早い。 記事
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Newtonsoft.Jsonのデシリアライズ時の挙動についてわかったこと

はじめに

本記事はNewtonsoft.jsonを使用した際に分かったデシリアライズ時の挙動についてわかったことをまとめた記事です。

わかったこと

Newtonsoft.jsonを使用してJson形式にシリアライズしたJsonオブジェクトをデシリアライズしたときに以下の挙動をしています。

①オブジェクトのコンストラクタに引数がある場合
コンストラクタの引数名がJSONオブジェクトの対応するプロパティ名と一致し(大文字と小文字を区別しない)、コンストラクタ内でプロパティの値の設定を行っていると、インスタンス生成時にプロパティが設定されます。以下のような場合です。

    #region 構築

    /// <summary>
    /// コンストラクタ
    /// </summary>
    public MetaData(string key)
    {
        Key = key;
    }

    #endregion

    #region プロパティ

    /// <summary>
    /// キー
    /// </summary>
    [JsonProperty]
    public string Key { get; }

    #endregion

では、デシリアライズ時に復元したい全てのプロパティ値をコンストラクタ引数に追加しなければならないのかというと、そうではありません。

② オブジェクトのコンストラクタに引数が無い場合
コンストラクタで引数を受け取りその引数を使用してプロパティ値の設定をしない場合は、インスタンスの生成後、オブジェクトのパブリックなプロパティまたは[JsonProperty]属性を付けたプロパティのセッターを使用して値を設定しようとします。

さいごに

どうやってデシリアライズされているのか仕組みを知らずに使っていたため、デバッグ中にうまくプロパティ値が復元できるプロパティとできないプロパティがあり混乱することがありましたので、本記事にまとめました。
皆様の参考になれば嬉しいです。
最後までお読みいただきありがとうございます。

参考サイト

https://www.it-swarm-ja.tech/ja/c%23/jsonnet%EF%BC%9A%E3%83%87%E3%83%95%E3%82%A9%E3%83%AB%E3%83%88%E3%81%AE%E3%82%B3%E3%83%B3%E3%82%B9%E3%83%88%E3%83%A9%E3%82%AF%E3%82%BF%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%9B%E3%81%9A%E3%81%AB%E3%83%87%E3%82%B7%E3%83%AA%E3%82%A2%E3%83%A9%E3%82%A4%E3%82%BA%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95%E3%81%AF%EF%BC%9F/1046211064/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

.NET frameworkのバージョンを上げる方法【Rider編】

.NET frameworkのバージョンによって使えないパッケージが存在します。
私はVisual StudioよりもRider派(Jetbrain社のIDE)なのですが、初めてで迷いました。
メモを残しておきます。

バージョンアップ方法

以下の流れでバージョンアップを行います。

2020-10-26_12h36_06.jpg

2020-10-26_12h43_21.jpg

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[.NET][C#]NUnitのAttribute_テストメソッド、テストクラス系

はじめに

今回はNUnitのAttributeで最も重要な
テストメソッド、テストクラスに付けるAttributeを調べてみた。

▼NUnit関連記事
[.NET][C#]NUnitを使用して単体テスト自動化 基本編
[.NET][C#]NUnitを使用した単体テストのカバレッジレポートを自動生成する
[.NET][C#]NUnitのClassic Modelアサーションメソッド一覧 ※Assertクラス
[.NET][C#]NUnitのStringAssertアサーションメソッド一覧
[.NET][C#]NUnitのCollectionAssertアサーションメソッド一覧
[.NET][C#]NUnitのFileAssert・DirectoryAssertアサーションメソッド一覧

実施環境

.NET:3.1.401
C#:8.0
NUnit:3.12.0

NUnit Attribute

ソースコードがテスト用のものであることを実行環境に伝えるマーキングで
テスト用のクラスやメソッドに付ける。
また、テスト用のパラメータの指定や事前・事後処理などの実行順序の定義するものなど、様々なAttributeがある。

今回はNUnitを実行するために必須である
テストクラス、テストメソッド系のAttributeを試す。

1. TestFixture

テストクラスであることを示すAttribute。

パラメータ無し
[TestFixture]
public class Tests {
    [Test]
    public void Test1()
    {
        Assert.Pass();
    }
}

上記のようにクラスに付ける。
クラスに[TestFixture]が付いているだけではテストが実行されるわけではなく、
[Test][TestCase]といったAttributeが付いたテストメソッドを持たないといけない。
逆にテストメソッドがあるのであれば[TestFixture]は無くてもいい。

型を指定
[TestFixture(typeof(string))]
public class Tests<T>
{
    [Test]
    public void TypeTest()
    {
        object obj = DummyTarget.getAnonymousObject();
        Assert.IsInstanceOf<T>(obj);
    }
}

上記のようにテストコード中の型を固定したくない場合は、
テストクラスにジェネリクスを付け、[TestFixture]の引数にType型を指定する。

パラメータ指定
[TestFixture("MBr", "マリオ", "ピーチ", "クッパ")]
[TestFixture("LoZ", "リンク", "ゼルダ", "ガノン")]
[TestFixture("Pok", "サトシ", "カスミ", "シゲル")]
public class ParamTestFixture
{
    readonly NintendoGameCharacter NCharacter;
    readonly string ExpectHero;
    readonly string ExpectHeroine;
    readonly string ExpectRival;

    public ParamTestFixture(string title, string hero, string heroine, string rival) {
        this.NCharacter = DummyTarget.getNintendoGameCharacter(title);
        this.ExpectHero = hero;
        this.ExpectHeroine = heroine;
        this.ExpectRival = rival;
    }

    [Test]
    public void NintendoHeroTest()
    {
        Assert.AreEqual(this.ExpectHero, NCharacter.Hero);
    }

    [Test]
    public void NintendoHeroineTest()
    {
        Assert.AreEqual(this.ExpectHeroine, NCharacter.Heroine);
    }

    [Test]
    public void NintendoRivalTest()
    {
        Assert.AreEqual(this.ExpectRival, NCharacter.Rival);
    }
}

上記のようにテストクラスのコンストラクタの引数を指定することもできる。
引数の型と数は、テストクラスのコンストラクタの引数と一致させる必要がある。
上記の場合、テスト実行時に各パラメータがコンストラクタに指定されたParamTestFixtureオブジェクトが3つ生成され、それぞれのテストメソッドが実行される。

テストクラス内の各メソッドで一貫したテストパラメータを使用したい場合は使うと良さそう。

2. TestFixtureSource

テストクラスに付けるAttribute。
変数等の要素をテストのパラメータとして指定することが可能。

変数をパラメータとして指定
[TestFixtureSource("ParamSource")]
public class ParamTestFixtureSource
{
    static object[] ParamSource =
    {
        new string[] {"MBr", "マリオ", "ピーチ", "クッパ"},
        new string[] {"LoZ", "リンク", "ゼルダ", "ガノン"},
        new string[] {"Pok", "サトシ", "カスミ", "シゲル"},
    };

    readonly NintendoGameCharacter NCharacter;
    readonly string ExpectHero;
    readonly string ExpectHeroine;
    readonly string ExpectRival;

    public ParamTestFixtureSource(string title, string hero, string heroine, string rival) {
        this.NCharacter = DummyTarget.getNintendoGameCharacter(title);
        this.ExpectHero = hero;
        this.ExpectHeroine = heroine;
        this.ExpectRival = rival;
    }

    [Test]
    public void NintendoHeroTest()
    {
        Assert.AreEqual(this.ExpectHero, NCharacter.Hero);
    }

    [Test]
    public void NintendoHeroineTest()
    {
        Assert.AreEqual(this.ExpectHeroine, NCharacter.Heroine);
    }

    [Test]
    public void NintendoRivalTest()
    {
        Assert.AreEqual(this.ExpectRival, NCharacter.Rival);
    }
}


クラス内のフィールド、プロパティ、メソッドをテスト用のパラメータとして指定できる。
フィールドの場合、上記のように変数名を指定する。
指定するパラメータはいずれもstaticである必要がある

上記例ではパラメータに配列を使用しているが、コレクション(IEnumerable実装クラス)でも可。
[TestFixture]でパラメータを指定するのと同様、
コンストラクタのメソッドシグニチャとパラメータの位置や型を一致させる必要がある。

外部クラスの変数をパラメータとして指定
class ParamWarehouse {
    static object[] ParamSource =
    {
        new string[] {"MBr", "マリオ", "ピーチ", "クッパ"},
        new string[] {"LoZ", "リンク", "ゼルダ", "ガノン"},
        new string[] {"Pok", "サトシ", "カスミ", "シゲル"},
    };
}

[TestFixtureSource(typeof(ParamWarehouse), "ParamSource")]
public class ParamTestFixtureSource2
{
    readonly NintendoGameCharacter NCharacter;
    readonly string ExpectHero;
    readonly string ExpectHeroine;
    readonly string ExpectRival;

    public ParamTestFixtureSource2(string title, string hero, string heroine, string rival) {
        this.NCharacter = DummyTarget.getNintendoGameCharacter(title);
        this.ExpectHero = hero;
        this.ExpectHeroine = heroine;
        this.ExpectRival = rival;
    }

    [Test]
    public void NintendoHeroTest()
    {
        Assert.AreEqual(this.ExpectHero, NCharacter.Hero);
    }

    [Test]
    public void NintendoHeroineTest()
    {
        Assert.AreEqual(this.ExpectHeroine, NCharacter.Heroine);
    }

    [Test]
    public void NintendoRivalTest()
    {
        Assert.AreEqual(this.ExpectRival, NCharacter.Rival);
    }
}

上記のように別クラスの変数、プロパティ、メソッドを指定することも可能。
[TestFixtureSource]の第一引数にパラメータを持つクラスのType型、第二引数に変数名などを指定。

使用できるパラメータの条件などは、パラメータのみ指定する場合と同じ。

メソッドでパラメータをロジカルに生成
[TestFixtureSource(nameof(ParamWarehouseMethod))]
public class ParamTestFixtureSource3
{
    readonly NintendoGameCharacter NCharacter;
    readonly string ExpectHero;
    readonly string ExpectHeroine;
    readonly string ExpectRival;

    public ParamTestFixtureSource3(string title, string hero, string heroine, string rival) {
        this.NCharacter = DummyTarget.getNintendoGameCharacter(title);
        this.ExpectHero = hero;
        this.ExpectHeroine = heroine;
        this.ExpectRival = rival;
    }

    [Test]
    public void NintendoHeroTest()
    {
        Assert.AreEqual(this.ExpectHero, NCharacter.Hero);
    }

    static object[] ParamGenerateMethod() {
        return new object[]
        {
            new string[] {"MBr", "マリオ", "ピーチ", "クッパ"},
            new string[] {"LoZ", "リンク", "ゼルダ", "ガノン"},
            new string[] {"Pok", "サトシ", "カスミ", "シゲル"},
        };
    }
}

上記のようにパラメータを生成するメソッドを指定することもできる。
上記例では単純にパラメータの配列を返しているだけだが、
ロジカルにパラメータを増幅、生成することも可能だ。

3. Test

テストメソッドであることを示すAttribute。

[Test]
public class TestTest {

    [Test]
    public void MarioBrosHeroTest()
    {
        string TargetGame = "MBr";
        string ExpectHero = "マリオ";

        Assert.AreEqual(ExpectHero, DummyTarget.getNintendoGameCharacter(TargetGame).Hero);
    }

    [Test(Description = "ポケモン主人公の妥当性をテストします")]
    public void PokemonHeroTest()
    {
        string TargetGame = "Pok";
        string ExpectHero = "サトシ";

        Assert.AreEqual(ExpectHero, DummyTarget.getNintendoGameCharacter(TargetGame).Hero);
    }

    [Test(ExpectedResult = "リンク")]
    public string ZeldaHeroTest()
    {
        string TargetGame = "LoZ";

        return DummyTarget.getNintendoGameCharacter(TargetGame).Hero;
    }
}

このAttributeを付けたメソッドがテストメソッドとして実行される。
下記のAttributeパラメータを指定できる。

Description:テストの説明
ExpectedResult:テスト想定結果

ExpectedResultを指定する場合は、メソッド中Assertクラスによるアサーションはせず、テスト対象の実行結果を戻り値として返すようにする。

簡単なテストなら上記で良いが、
後記の[TestCase]では上記以上のことができるため、
[Test]は使わず[TestCase]で統一していいかもしれない。

4. TestCase

テストメソッドであることを示すAttribute。
[Test]よりも多機能。

[TestCase]
public class TestCaseTest {

    [TestCase]
    public void MarioBrosHeroTest()
    {
        string TargetGame = "MBr";
        string ExpectHero = "マリオ";

        Assert.AreEqual(ExpectHero, DummyTarget.getNintendoGameCharacter(TargetGame).Hero);
    }

    [TestCase("MBr", 1983, "マリオ")]
    [TestCase("LoZ", 1986, "リンク")]
    [TestCase("Pok", 1996, "サトシ")]
    public void NintendoHeroTest(string targetGame, int expectFirst, string expectHero)
    {
        NintendoGameCharacter NCharacter = DummyTarget.getNintendoGameCharacter(targetGame);

        Assert.AreEqual(expectFirst, NCharacter.FirstAppearance);
        Assert.AreEqual(expectHero, NCharacter.Hero);
    }

    [TestCase("MBr", ExpectedResult="ピーチ")]
    [TestCase("LoZ", ExpectedResult="ゼルダ")]
    [TestCase("Pok", ExpectedResult="カスミ")]
    public string NintendoHeroineTest(string targetGame)
    {
        return DummyTarget.getNintendoGameCharacter(targetGame).Heroine;
    }
}

[TestCase]のみの指定であれば[Test]のみ指定するのと同じ。
上から2番目の例のようにテストメソッドに引数を指定することが可能。
また、3番目のように引数と想定結果を指定することも可能。

[TestCase]の様々な名前付きパラメータ
    [TestCase(
    //▼テストケースの作者
    Author="suganury"
    //▼カテゴリ. dotnet test --filter TestCategory={CategoryName}のオプションで特定のカテゴリのみテスト実行可能
    , Category="hogeCategory"
    //▼テストケースの説明
    , Description="説明だYO"
    //▼テストを実行しないプラットフォームをカンマ区切りで指定。OSや.NETバージョンなどを指定可能
    , ExcludePlatform="Net-1.0,Windows8"
    //▼想定結果。Assertクラス等によるアサーションではなく、戻り値に実行結果を返すようにする。
    , ExpectedResult="hoge"
    //▼テスト無効フラグ。trueにするとテストが実行されなくなる
    , Explicit=false
    //▼テスト無効理由。Explicitとセットで使う
    , Reason="hogeしか返ってこんし"
    //▼テスト無視フラグと理由。このAttributeが付いているとテストが実行されなくなる
    , Ignore="hogeしか返ってこんし"
    //▼Ignoreと全く同じ。どちらか1つでいい
    , IgnoreReason="hogeしか返ってこんし"
    //▼テスト名。デフォルトではメソッド名になる
    , TestName="Hogeテスト"
    //▼対象のテストクラス
    , TestOf=typeof(String)
    )]
    public string test(){
        return "hoge";
    }

ExpectedResultの他にも様々なAttributeパラメータが指定できる。
あまり指定しすぎるとゴチャゴチャするので、
[Category]などはそれ用のAttributeを指定するのがいいかもしれない。

5. TestCaseSource

テストメソッドであることを示すAttribute。
変数等の要素をテストのパラメータとして指定することが可能。
[TestFixtureSource]のテストメソッド版。

クラス内の変数をパラメータとして指定
public class TestCaseSourceTest {

    static object[] NintendoCharaCases =
    {
        new object[] { "MBr", 1983, "マリオ" },
        new object[] { "LoZ", 1986, "リンク" },
        new object[] { "Pok", 1996, "サトシ" }
    };

    static IList<object> NintendoCharaCaseList = new List<object>
    {
        new object[] { "MBr", 1983, "マリオ" },
        new object[] { "LoZ", 1986, "リンク" },
        new object[] { "Pok", 1996, "サトシ" }
    };

    [TestCaseSource("NintendoCharaCases")]
    [TestCaseSource("NintendoCharaCaseList")]
    public void NintendoCharaTest(string targetGame, int expectFirst, string expectHero)
    {
        NintendoGameCharacter NCharacter = DummyTarget.getNintendoGameCharacter(targetGame);

        Assert.AreEqual(expectFirst, NCharacter.FirstAppearance);
        Assert.AreEqual(expectHero, NCharacter.Hero);
    }
}

上記例のように自クラスの配列もしくはコレクションをテストメソッドのパラメータとして指定できる。
パラメータとして指定するものはstaticである必要がある
使用時の制約としては、[TestFixtureSource]と概ね同じ。

外部のクラスの変数をパラメータとして指定
public class TestCaseSourceOtherClass {

    [TestCaseSource(typeof(TestCaseSourceTest), "NintendoCharaCases")]
    public void NintendoCharaTestOtherClass(string targetGame, int expectFirst, string expectHero)
    {
        NintendoGameCharacter NCharacter = DummyTarget.getNintendoGameCharacter(targetGame);

        Assert.AreEqual(expectFirst, NCharacter.FirstAppearance);
        Assert.AreEqual(expectHero, NCharacter.Hero);
    }
}

上記のように外部のクラスが持つstaticフィールド等をパラメータとして指定することも可能。

メソッドでパラメータをロジカルに生成
    [TestCaseSource(nameof(GenerateTestCharacter), new object[] { "Hero" })]
    public void NintendoHeroTest(string expectHero)
    {
        List<string> nintendoHeros = DummyTarget.nintendoChara
                                                .Values
                                                .Select(n => n.Hero)
                                                .ToList();

        CollectionAssert.Contains(nintendoHeros, expectHero);
    }

    static IList<string> GenerateTestCharacter(string charaType)
    {
        IList<string> list = new List<string>();
        if(charaType.Equals("Hero")) {
            list.Add("マリオ");
            list.Add("リンク");
            list.Add("サトシ");
            list.Add("ロックマン");
            list.Add("クラウド");
        } else if(charaType.Equals("Heroine")) {
            list.Add("ピーチ");
            list.Add("ゼルダ");
            list.Add("カスミ");
            list.Add("ロール");
            list.Add("ティファ");
        } else if(charaType.Equals("Rival")) {
            list.Add("クッパ");
            list.Add("ガノン");
            list.Add("シゲル");
            list.Add("フォルテ");
            list.Add("セフィロス");
        }
        return list;
    }

また、上記のように[TestCaseSource]の第一引数に配列・コレクションを返すメソッド、第二引数にそのメソッドの引数を指定することで、
パラメータをロジカルに生成することが可能。
上記のテスト実行結果は下記となる。

実行結果
NUnit Adapter 3.15.1.0: Test execution complete
  √ NintendoHeroTest("マリオ") [5ms]
  √ NintendoHeroTest("リンク") [1ms]
  √ NintendoHeroTest("サトシ") [< 1ms]
  X NintendoHeroTest("ロックマン") [27ms]
  エラー メッセージ:
     Expected: some item equal to "ロックマン"
  But was:  < "マリオ", "リンク", "サトシ" >

  X NintendoHeroTest("クラウド") [< 1ms]
  エラー メッセージ:
     Expected: some item equal to "クラウド"
  But was:  < "マリオ", "リンク", "サトシ" >

場合によっては様々なパターンのパラメータをテストすることもあるだろうし、それをテストコードにハードコードするのは苦しい場面もあるだろう。
そういったときにこれは便利な機能だと思う。

まとめ

今回記載したAttributeとアサーションメソッドを知っておけば
最低限のテスト実施自体はできると思う。
が、やはりテストのパフォーマンス等を求めるとなると事前・事後処理用のAttributeは欲しいし、
カテゴライズしたり無効にしたりするAttributeも使う機会はあるだろう。

そこらへんはまた今度:tea:

参考

NUnit公式:Attribute一覧ページ

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む