研究者へのテストコードのススメ（研究者だからむしろ正しい研究結果のために実践しよう）

# はじめに
### 対象者
 - 研究者
 - MatlabやPythonでシミュレーション書いている人
 - __実機を実装している/しようとしている人__
 - 

### コメント
テストケース初心者です．
今の俺に絶対必要だ！と思ってはじめました．
一応ある会社でインターンしてた時Rubyで少し触れたぐらいの記憶です．
自分のメモのためにの記述です．

# きっかけ
現在，自分がドローンの研究をしていることもあり，
Matlabでシミュレーション， C++に実装（実現不可だったので）， Simulinkでの実装という風に
いくつか言語化跨る上にコードの改変が非常に多いので，あっちこっち編集してたら数ヶ月無駄にしたのでテストコードを書くことを決心した．

# 利点
 - あっちこっち編集してもモジュール一つ一つは「正確に動く」ことが保証できる
 - 一気にコード書かずに細かく細かくモジュール単位で考えられるようになる
 - 汎用性の高いモジュール（クラス）を設計しようと心がけるため，結局一つ作れば色んなところで使えるようになる
    - カルマンフィルタ
    - 制御器
    - ドローンのモデル, モータのモデル etc..
 - 指導教員に__「なんでそこまでは正しいと言えるの？」__と聞かれても__「外部装置と比較した値を使ったテストコードがエラーを履かないので自分のコードが間違っていない限り正しいと言える」__とさらっと攻撃を避けることができる
 - 研究に信憑性を
 - 実機に実装する時，__[大体どのくらいの値に収まるか」__，__「どういうパラメータが欲しいか・計測しなければいけないのか」__がシミュレーション書く時から見えてくるので設計の見通しがしやすい．

# テストケース書かないと
 - どこがエラってるのか分からない
 - 一個一個手で実行していく必要がある
 - ひとつのモジュールを変えて他が動くなっても，変えた方のモジュールのみ疑うようになり最終的に奥底に潜む問題点にたどり着かない
 - 結果，時間が無駄に過ぎていく

#　テストケースの書き方
## Matlab編
参考資料：
 - [MATLAB Unit Testing Framework](https://jp.mathworks.com/videos/matlab-unit-testing-framework-74975.html)
 - [runtests](https://jp.mathworks.com/help/matlab/ref/runtests.html)

### チャチャっと書いた数行のコードをテストしたい（[runtests](https://jp.mathworks.com/help/matlab/ref/runtests.html)）
#### フォルダ内全部をテスト
以下のようなコードを用意

```matlab
%% Test size
exp = [7 13];
act = ones([7 13]);
assert(isequal(size(act),exp))
```
以下で実行
`runtests`

#### 1つのmファイル
以下のようなmファイルを用意

```matlab
function tests = runtestsExampleTest
tests = functiontests(localfunctions);

function testA(testCase)
verifyEqual(testCase,5,5);

function testB(testCase)
verifyTrue(testCase,logical(1));
```
以下で実行
` results = runtests('runtestsExampleTest.m');` 

### いくつかの関数・mファイル・クラステストをまとめて実行したい（[testsuite](https://jp.mathworks.com/help/matlab/ref/testsuite.html)）
#### フォルダ内全部
`suite = testsuite`

#### mファイル1つのみ
以下のようなmファイル1つ用意

```matlab
function tests = eyeTest
tests = functiontests(localfunctions);

function doubleClassTest(testCase)
actValue = eye;
verifyClass(testCase,actValue,'double')

function singleClassTest(testCase)
actValue = eye('single');
verifyClass(testCase,actValue,'single')

function uint16ClassTest(testCase)
actValue = eye('uint16');
verifyClass(testCase,actValue,'uint16')

function sizeTest(testCase)
expSize = [7 13];
actValue = eye(expSize);
verifySize(testCase,actValue,expSize);

function valueTest(testCase)
actValue = eye(42);
verifyEqual(testCase,unique(diag(actValue)),1)    % diagonal are 1s
verifyEqual(testCase,unique(triu(actValue,1)),0)  % upper tri vals are 0
verifyEqual(testCase,unique(tril(actValue,-1)),0) % lower tri vals are 0
```
以下でsuite作成
`suite = testsuite('eyeTest')`
以下でテスト実行
`result = run(suite)`

#### [いくつかのファイルをまとめたい](https://jp.mathworks.com/help/matlab/ref/matlab.unittest.testsuite-class.html)
```matlab
% 現在のインポートリストにmatlab.unittest.TestSuiteクラスを追加
import matlab.unittest.TestSuite;
% テストスイート作成
fileSuite    = TestSuite.fromFile('SomeTestFile.m'); 
folderSuite  = TestSuite.fromFolder(pwd);
packageSuite = TestSuite.fromPackage('mypackage.subpackage');
classSuite   = TestSuite.fromClass(?mypackage.MyTestClass); 
methodSuite  = TestSuite.fromMethod(?SomeTestClass,'testMethod');
% 全体をまとめるSuite作成
largeSuite = [fileSuite, folderSuite, packageSuite, classSuite, methodSuite];
% 実行
result = run(largeSuite)
```

### 自分で定義したクラスのテスト（KalmanFilter.mなど)
#### [クラスをしようするセットアップコードと破棄](https://jp.mathworks.com/help/matlab/matlab_prog/write-setup-and-teardown-code-using-classes.html)
```matlab
classdef BankAccountTest < matlab.unittest.TestCase
    % Tests the BankAccount class.
    
    methods (TestClassSetup)
        function addBankAccountClassToPath(testCase)
            p = path;
            testCase.addTeardown(@path,p);
            addpath(fullfile(matlabroot,'help','techdoc','matlab_oop',...
                'examples'));
        end
    end
    
    methods (Test)
        function testConstructor(testCase)
            b = BankAccount(1234, 100);
            testCase.verifyEqual(b.AccountNumber, 1234, ...
                'Constructor failed to correctly set account number');
            testCase.verifyEqual(b.AccountBalance, 100, ...
                'Constructor failed to correctly set account balance');
        end
        
        function testConstructorNotEnoughInputs(testCase)
            import matlab.unittest.constraints.Throws;
            testCase.verifyThat(@()BankAccount, ...
                Throws('MATLAB:minrhs'));
        end
        
        function testDesposit(testCase)
            b = BankAccount(1234, 100);
            b.deposit(25);
            testCase.verifyEqual(b.AccountBalance, 125);
        end
        
        function testWithdraw(testCase)
            b = BankAccount(1234, 100);
            b.withdraw(25);
            testCase.verifyEqual(b.AccountBalance, 75);
        end
        
        function testNotifyInsufficientFunds(testCase)
            callbackExecuted = false;
            function testCallback(~,~)
                callbackExecuted = true;
            end
            
            b = BankAccount(1234, 100);
            b.addlistener('InsufficientFunds', @testCallback);
            
            b.withdraw(50);
            testCase.assertFalse(callbackExecuted, ...
                'The callback should not have executed yet');
            b.withdraw(60);
            testCase.verifyTrue(callbackExecuted, ...
                'The listener callback should have fired');
        end
    end
end
```

## Simulink編
参考資料：
 - [Simulink and MALTAB Unit Testing Frameword](https://blogs.mathworks.com/simulink/2017/05/17/simulink-and-the-matlab-unit-testing-framework/)
 - [Test Models Using MATLAB Unit Test](https://jp.mathworks.com/help/sltest/ug/run-test-files-using-matlab-unit-test.html?action=changeCountry&s_tid=gn_loc_drop)

教えてください・・・・


## Python編
```python
import unittest

def Addition(a, b):
    return a+b

class AdditionTests(unittest.TestCase):
    def testEqual(self):
       a = 1
       b = 2
       c = Addition(1,2)
       self.assertTrue(c==3)

if __name__ == "__main__":
    unittest.main()
```