ユニットテストの作り方 Part.1 導入のためのデザインパターン


→ [Part.2はこちら](http://qiita.com/progrommer/items/6b764ebd71ed08fb9295)

ユニットテストを実行するためには、デバイスなどの環境依存要素を代替・排除する必要がある。
これらの取り扱いを注意深く設計しておかないと、ユニットテスト不能になってしまう。
本タイトルの一連の記事ではデザインパターンを用いながら、テストと実働のための実装を考える。
本記事では、オンメモリーで持つデータの扱いを考えるが、ネットワークなどのデバイスの場合も同じである。

突き詰めると、どのようにテスト用モジュールへの差し替えを行うかが焦点となる。
この考えは、Dependency Injection (DI) と呼ばれている。
DI では、Interface を介してモジュール間の結合度を下げる。

サンプルプログラムは C++ で記述しているが、C# や Python も書く予定。

## 変更履歴
2015-04-26 Pythonのサンプルコードを追加


# 環境依存なデータ

データは、アプリケーションの動作を決めるものだったり、動作の結果を伝えるものだったり様々である。
ローカルで済めば良いが、どうしてもグローバルに使用したい場合がある。どのように保持すべきだろうか。

次の例では、外部モジュールのバージョンによって動作を変える機能を想定している。

```cpp
// 導入前（テスト不能、または困難）
void func() {
    // バージョン取得
    int version = SomeModule::getMajorVersion();
    if (version > 10) {
        // 指定バージョンの時に、何か処理
    }
}
```

ここで、`SomeModule::getMajorVersion()` は特定環境でしか使えなかったり、状態を切り替えるのが大変だったりするものとする。このようなコードでは、続く if 文のテストは難しい。


# Proxy パターンの利用

ここでは `SomeModule` を置き換えるダミーのクラスを作り、実体とダミーにアクセスする Proxy を用意してテスト可能にしてみる。
まずは、窓口となるProxyクラスのInterfaceから。

```cpp
class ModuleProxy {
	virtual int getMajorVersion_() const = 0;
protected:
	ModuleProxy() {}
public:
	virtual ~ModuleProxy() {};
	int getMajorVersion() const {
		return this->getMajorVersion_();
	}
};
```

次は実体クラスとダミークラス。
ここでは、元々のSomeModuleも隠蔽して、利用クラスを作っている。

```cpp
// 実際に外部モジュールを呼び出すクラス
class SomeModuleImpl : public ModuleProxy {
	virtual int getMajorVersion_() const override {
		return SomeModule::getMajorVersion();
	}
};

// テスト用ダミークラス
class ModuleDummy : public ModuleProxy {
	virtual int getMajorVersion_() const override {
		return major_;
	}
public:
	ModuleDummy(int ver = 0)
		: major_(ver)
	{}
	int major_;
};
```

func関数と、テストコードは次のようになる。

```cpp
void func(ModuleProxy& module) {
    // バージョン取得
    int version = module.getMajorVersion();
    if (version > 10) {
    }
}

void XXXTest::funcTest() {
   	ModuleDummy dummy(10);
    {
        func(dummy);
    }
    {
    	dummy.major_ = 11;
        func(dummy);
    }
}
```



# Monostate パターンの利用

別の形として Monostate パターンを使用して実装を行ってみる。
ここではメンバー関数も static にしているが、通常の関数でもかまわない。

```cpp
class Data {
    static int majorVersion;
public:
    static int getMajorVersion() const {
        return majorVersion;
    }

    friend class XXXTest;
};
int Data::majorVersion = 1;
```

これを使うアプリケーション実装は次のようになる。

```cpp
void func() {
    if (Data::getMajorVersion() > 10) {
    }
}
```

テスト側はこのようになる。

```cpp
void XXXTest::funcTest() {
    {
        Data::majorVersion = 10;
        func();
    }
    {
        Data::majorVersion = 11;
        func();
    }
}
```

Monostate パターンはシンプルだが、値の初期化を考える必要がある。
他の static な要素から参照される場合は、初期化の順番に注意しなくてはならない。


# サンプルコード（Python）

Monostate パターンの Python 版を次に示す。
メソッドには classmethod を指定した。

```python
class Data:
    _majorVersion = 1

    @classmethod
    def getMajorVersion(cls) -> int:
        return cls._majorVersion
```

テスト側は、次のようになる。

```python
import unittest

class Test_testSample(unittest.TestCase):
    def test_version(self):
        Monostate.Data._majorVersion = 10
        self.assertFalse(Target.func())
        Monostate.Data._majorVersion = 11
        self.assertTrue(Target.func())

```


# まとめ

本記事では Proxy パターンを用いて DI を実現した。
また、よりシンプルにMonostate パターンを使用する方法も示した。


# 次回以降のネタ

+ 契約プログラミング

契約の機能をサポートしない、C++/C#/Python などの言語であっても、契約を意識しておくことは重要である。

+ Proxy パターンと Monostate パターンの融合

Monostate はポリモルフィズム的に使えるので、Proxy パターンを取り込んでより使いやすくすることも可能である。


+ ユニットテストで friend を使う (C++)

ユニットテストで friend を使うのは良いが、アプリケーション側の header に friend を追加することになるため、コンパイルのやり直しが発生してしまう。その対処方法を考える。


+ shared_ptrとweak_ptrを使う (C++)

リソースアクセスをshared_ptrで実現する場合に、ProxyのIFとしてはweak_ptrを使った方がよいことがある。
テスト可能な状態のまま、このような機能拡張を行ってみる。