2012-10-07 5 views
6

私はgen_fsmで実装されたFSMを持っているとします。いくつかのStateNameのいくつかのイベントに対して、私はデータベースにデータを書き込んで、結果をユーザーに返すべきです。したがって、次のステート名が関数で表される。OTPの原則。実際に機能コードと非機能コードを分離する方法は?

my_db_module
statename(Event, _From, StateData) when Event=save_data-> 
    case my_db_module:write(StateData#state.data) of 
     ok -> {stop, normal, ok, StateData}; 
     _ -> {reply, database_error, statename, StateData) 
    end. 

:書き込みが実際のデータベース書き込みを実現非機能コードの一部です。

このコードには2つの大きな問題があります。まず、FSMの純粋な機能概念が非機能コードの一部で混在しているため、FSMの単体テストも不可能になります。第2に、FSMを実装するモジュールは、my_db_moduleの特定の実装に依存します。私の意見で

、2つの解決策が考えられます

  1. はmy_db_module実装します、ステート名に返信StateData中から保存、wait_for_db_answerに切り替わりません、データベースを扱ういくつかのプロセスに非同期メッセージを送信するようwrite_asyncをし、 handle_info内のメッセージとしてdb管理プロセスの結果を待ちます。

    このような実装の利点は、実際のデータベースに触れずにeunitモジュールから任意のメッセージを送信できることです。この解決策は、dbが先に応答すると、FSMが状態を変更したり、別のプロセスがsave_dataをFSMに送信したりする可能性がある競合状態に悩まされます。このソリューションは、競合状態に苦しんでいませんが、FSMは、多くのコールバックを使用している場合、それは本当にコードを圧倒

    init([Callback]) -> 
    {ok, statename, #state{callback=Callback}}. 
    
    statename(Event, _From, StateData) when Event=save_data-> 
        case StateData#state.callback(StateData#state.data) of 
         ok -> {stop, normal, ok, StateData}; 
          _ -> {reply, database_error, statename, StateData) 
    end. 
    

  2. はStateData内のinit/1の間に書かれたコールバック関数を使用します。実際の関数コールバックに変更することで単体テストが可能になりますが、関数コードの分離の問題は解決しません。

このソリューションはすべて快適ではありません。純粋なOTP/Erlangの方法でこの問題を処理する方法がいくつかありますか? OTPとeunitの原則を過小評価することが私の問題です。

答えて

2

これを解決する1つの方法は、データベースモジュールの依存性注入を使用する方法です。

あなたが

-record(state, { ..., db_mod }). 

としてあなたの状態のレコードを定義そして今、あなたはgen_serverのINIT/1時db_modを注入することができます

statename(save_data, _From, 
      #state { db_mod = DBMod, data = Data } = StateData) -> 
    case DBMod:write(Data) of 
    ok -> {stop, normal, ok, StateData}; 
    _ -> {reply, database_error, statename, StateData) 
    end. 
:私たちはあなたのコードを持っているときに

init([]) -> 
    {ok, DBMod} = application:get_env(my_app, db_mod), 
    ... 
    {ok, #state { ..., db_mod = DBMod }}. 

私たちは、別のモジュールでテストするときにデータベースモジュールを無効にすることができます。スタブの挿入は非常に簡単で、データベースコードの表示を変更することができます。

もう1つの方法は、テスト中にmeckのようなツールを使用してデータベースモジュールをモックすることですが、通常は構成可能にすることをお勧めします。

一般的に、複雑なコードを分割して別々にテストできるように、独自のモジュールに分割する傾向があります。私はめったに他のモジュールの単体テストを行うことはほとんどなく、そのような部品のエラーを処理するために大規模な統合テストを好む。 Common Test、PropEr、Triq、Erlang QuickCheckを見てください(後者はオープンソースではなく、フルバージョンは無料です)。

関連する問題