2

基本的にファイルからx行を読み取り、データベースでそれを見つけようとし、ファイル内の情報の一部を取得して、データベース内の行の情報を更新するプログラムを作成する必要があります。

他のすべてが失敗した場合は、db に新しい行として挿入します。

したがって、基本的には多くの挿入と更新が行われます。

多くのフィールドを更新しているので、各情報が正しい場所にあることを確認したいので、ユニットテストを試みています(つまり、メールアドレスがファーストネームなどで設定されていません)。

ただし、これがプログラムの方法であることがわかっているため、どうすればよいかわかりません

// get records from file

foreach record in file
{
   db record = find if it is in db

   if(record != null)
   {
        if(do another logic check)
         {
             // update record
         }
         else if(do another logic check)
         { 
            // update record
         }
        else
        {
           // do some more logic 

            if(do another check)
             {
               // update record
             }
        }
   }
   else
   {
      // do some more logic checks and do inserts.
   }
}

これは、おそらくいくつかのプライベートメソッド(更新レコード部分など)を含むvoidメソッドで見られます。これを単体テストするにはどうすればよいですか?最初のif(別のロジックチェックを行う)を単体テストし、送信したレコードがそれらの条件を満たしているとします。

ただし、これらはプライベート メソッドであるため、単体テストを行うことはできません。現在、メソッドが何百ものレコードを通過し、おそらくほとんどのものをエラー ログ ファイルに出力するため、何も返されません。か何か。

お見せしたコードはサービス層にあります。moqでdbメソッド呼び出しをモックアウトします。

アプリケーションはコンソール アプリです。

ロジックを確認できるように、これを分割する方法について何か提案はありますか?

4

4 に答える 4

2

db 呼び出しをモックすることもできます。また、レコード タイプをモックして、メソッド内でモックを渡すこともできます。

于 2012-10-04T16:23:12.287 に答える
0

私があなたに見せたコードはサービスレイヤーにあります。私はmoqでdbメソッド呼び出しをモックアウトします。

それが鍵です。単体テストでモックできるインターフェースの背後にあるすべてのデータベース依存関係を抽象化する必要があります。メソッドに対する期待を定義します。ファイルの読み取りに関する限り、ファイル名を使用せずに、 StreamまたはStreamReader代わりに、実際の存在に依存するのではなく、単体テストでサンプルファイルデータをフィードできるようにメソッドを抽象化する必要があります。ファイル。

したがって、クラスは次のようになります。

public class SomeClass
{
    private readonly IRepository _repo;
    public SomeClass(IRepository repo)
    {
        _repo = repo;
    }

    public void SomeMethod(StreamReader reader)
    {
        string line;
        while ((line = reader.ReadLine()) != null)
        {
            // put your logic and use the _repo to update your database.
        }
    }
}
于 2012-10-04T16:19:38.083 に答える
0

あなたのクラスは、高レベルから次のように見えるかもしれません:

// get records from file

foreach record in file
{
   dbRecord = _db.Find(record)

   if(dbRecord != null)
   {
        if(logicCheck(dbRecord))
        {
            _db.Update(dbRecord)
        }
        else if(logicCheck2(dbRecord))
        { 
            _db.Update3(dbRecord)
        }
        else
        {
            // do some more logic 
        }
        if(otherCheck(dbRecord))
        {
            _db.Update2(dbRecord)
        }
   }
   else
   {
      // do some more logic checks
      _db.Insert(record)
   }
}

テストは次のようになります。

public void TestInsert()
{
    _mock.Setup(r => r.Find(record).Returns(null))

    class = new class(_mock.Object);
    class.methodToTest(fileRecord);

    _mock.Verify( r => r.Insert(record));
}

public void TestUpdate()
{
    var dbRecordThatPassesLogicCheck = new dbRecord(// initialize for test)
    _mock.Setup(db => db.Find(record).Returns(dbRecordThatPassesLogicCheck))

    class = new class(_mock.Object);
    class.methodToTest(fileRecord);

    _mock.Verify( r => r.Update(dbRecordThatPassesLogicCheck));
}

public void TestUpdate3()
{
    var dbRecordThatPassesLogicCheck2 = new dbRecord(// initialize for test)
    _mock.Setup(db => db.Find(record).Returns(dbRecordThatPassesLogicCheck2))

    class = new class(_mock.Object);
    class.methodToTest(fileRecord);

    _mock.Verify( db => db.Update3(dbRecordThatPassesLogicCheck2));
}

ここで Moq (またはその他のモッキング フレームワーク) が購入できるのは、クラスが、logicCheck が true の場合に Update を呼び出すか、または logicCheck2 がパスしても logicCheck1 がパスしない場合に Update3 を呼び出すかをシミュレートする機能です。プライベート メソッドをモック アウトするのではなく、find メソッド (モックしているリポジトリ) からのプリセット入力を使用してそれらを実行します。

クラスはおそらく、最初のレコード get の取得で 2 番目のインターフェイス依存関係を取得する必要があります。これにより、コードがより分離され (ファイル システムが web サービスを読み取る変更を読み取っても、クラスは直接影響を受けなくなります)、より多くのテスト容易性が可能になります。

于 2012-10-04T17:07:32.630 に答える
0

依存関係が注入された小さなクラスをたくさん使用する傾向があります。これにより、使いやすさとテスト容易性の継ぎ目が提供されます。また、if .. else if .. else ブロックを注入可能な Strategy オブジェクトに抽出する傾向があります。これにより、それらを使用するプロセスの外でロジックをテストできるようになります。

これにより、ファイルの読み取りに関する懸念が分離されます

class RecordFileReader
{
    void UpdateAll(file)
    {
        foreach (var record in file)
        {
            _fileRecordImporter.Import(record)
        }
    }
}

単一ファイルレコードの処理から

class FileRecordImporter
{   
    void Import(record)
    {
        db_record = find if it is in db
        if (db_record != null)
        {
            _dbRecordUpdater.Update(db_record, record)
            return;
        }
        _dbRecordCreator.CreateFrom(record);
    }
}

更新のためにレコードからデータベースに情報を転送する方法を決定する

class DbRecordUpdater
{
    void Update(db_record, record)
    {
        var recordUpdater = _recordUpdaters.FirstOrDefault(x=>x.IsMatch(db_record, record));
        if (recordUpdater != null)
        {
            recordUpdater.Update(db_record, record)
        }
    }
}

とインサート

class DbRecordCreator
{
    void CreateFrom(record)
    {
        var recordCreator = _recordCreators.FirstOrDefault(x=>x.IsMatch(record));
        if (recordCreator != null)
        {
            var db_record = recordCreator.Create(record)
            ...
        }
    }
}
于 2012-10-04T18:46:26.660 に答える