2017-12-16 23 views
0

私はElixir/Phoenixサードパーティ製のモジュールを使って遊んできました。 (サードパーティのサービスからのいくつかのデータをフェッチするために使用されているモジュール)、これらのモジュールの一つは、そうのように見える:実際のデータは私の質問には関係ありませんElixir/Phoenixサービスモジュールのテスト

module TwitterService do 
    @twitter_url "https://api.twitter.com/1.1" 

    def fetch_tweets(user) do 
    # The actual code to fetch tweets 
    HTTPoison.get(@twitter_url) 
    |> process_response 
    end  

    def process_response({:ok, resp}) do 
    {:ok, Poison.decode! resp} 
    end 

    def process_response(_fail), do: {:ok, []} 
end 

。ここでは、テストでいくつかのテストを失敗させるために、@twitter_urlモジュール変数を動的に設定する方法に興味があります。例:

module TwitterServiceTest 
    test "Module returns {:ok, []} when Twitter API isn't available" 
    # I'd like this to be possible (coming from the world of Rails) 
    TwitterService.configure(:twitter_url, "new_value") # This line isn't possible 
    # Now the TwiterService shouldn't get anything from the url 
    tweets = TwitterService.fetch_tweets("test") 
    assert {:ok, []} = tweets 
    end 
end 

これを行うにはどうすればよいですか? 注::configs@twiter_urlに別々に設定するには、devtestの環境で個別に設定することができますが、Twitter APIの実際の応答についてもテストできますテスト環境。
私が思いついた解決策の1つは

def fetch_tweets(user, opts \\ []) do 
    _fetch_tweets(user, opts[:fail_on_test] || false) 
end 

defp _fetch_tweets(user, [fail_on_test: true]) do 
    # Fails 
end 

defp _fetch_tweets(user, [fail_on_test: false]) do 
    # Normal fetching 
end 

だったしかし、それはちょうどこれによりよい解決策がなければならない、ハックと愚かなようです。

答えて

2

それはMocks And Explicit Contractsにホセによって提案されたとして、最良の方法は、依存性注入を使用して、おそらく次のようになります。必要なときに

module TwitterService do 
    @twitter_url "https://api.twitter.com/1.1" 

    def fetch_tweets(user, service_url \\ @twitter_url) do 
    # The actual code to fetch tweets 
    service_url 
    |> HTTPoison.get() 
    |> process_response 
    end  

    ... 
end 

は今テストであなただけの別の依存関係を注入:

# to test against real service 
fetch_tweets(user) 

# to test against mocked service 
fetch_tweets(user, SOME_MOCK_URL) 

このアプローチにより、将来別のサービスを簡単に接続することもできます。プロセッサの実装は、サービスが何らかの契約に従っていると仮定して、基礎となるサービスに依存すべきではありません(このような特殊なケースでは、jsonにURLを指定して応答します)。

1

configのように聞こえます。テストで実行時にconfig内の値を変更し、テスト後にその値を復元することができます。

最初に、実際のコードでは@twitter_urlの代わりにApplication.get_env(:my_app, :twitter_url)を使用してください。

次に、あなたのテストで、あなたはこのようなラッパー関数を使用することができます。do、あなたのテストで今

def with_twitter_url(new_twitter_url, func) do 
    old_twitter_url = Application.get_env(:my_app, :twitter_url) 
    Application.set_env(:my_app, :twitter_url, new_twitter_url) 
    func.() 
    Application.set_env(:my_app, :twitter_url, old_twitter_url) 
end 

を:

with_twitter_url "<new url>", fn -> 
    # All calls to your module here will use the new url. 
end 

あなたが非同期テストを使用していないことを確認してくださいこれは地球環境を変える技術です。

+0

これはjavascript-yの処理方法のようです。私はこれの背後にあるアイデアが気に入っていますが、唯一の懸念は、これを使って非同期ではないテストを行うことです。 これは、誰かがasyncを忘れたときにバグを引き起こす可能性があります:テストでtrue(複数の開発者がプロ​​ジェクトで作業している場合) –

+0

'async'はデフォルトではfalseであるため、明示的に破棄する必要があります。しかし、あなたのユースケース(あまりにも多くのエンドポイントではありません)には実用的な場合は、ええ、私は@ mudasobwaのソリューションと一緒に行きます。 – Dogbert

+1

これは、モジュール実装のコードを変更したくない場合に有効です。ここで、これを適用します。 –

関連する問題