2012-02-23 7 views
8

私は、現在の日付に応じてDBからフェッチされたデータに異なるロジックを実装する方法を持っています。ユニットテストJavaの時間ベースのロジック

ユニットテストでオブジェクトを作成し、オブジェクトをDBに保存し、テストされたメソッドを呼び出すことで、テストしたいと考えています。しかし、予測可能な結果を​​得るためには、毎回システムの日付を変更する必要があり、Javaでそれを行う方法がわかりません。

提案?

+2

実際にパラメータとして時間を与えるようにメソッドを変更し、必要な時間を与えてテストすることができます。何がこれをもう一度話すのですか? – belgther

+0

現在の日時はどのようになっていますか?たぶんモックがうまくいくでしょう... –

+0

今は 'new Date()'だけで、おそらく私はTimeProviderクラスが必要でしょう – Alex

答えて

14

現在の日付を使用して、期待される結果を生成することができます。

または、テストするときに(時計ではなく)与えた日付/時刻を使用するようにシステムを書いてください。そうすることで、テストはいつも期待している時間になります。

私は、データが、例えば駆動することができるFixedTimeSourceを使用するテストでは

interface TimeSource { 
    long currentTimeMS(); // actually I have currentTimeNS 
    void currentTimeMS(long currentTimeMS); 
} 

enum VanillaTimeSource implements TimeSource { 
    INSTANCE; 

    @Override 
    public long currentTimeMS() { 
     return System.currentTimeMillis(); 
    } 

    @Override 
    public void currentTimeMS(long currentTimeMS) { 
     // ignored 
    } 
} 

class FixedTimeSource implements TimeSource { 
    private long currentTimeMS; 
    @Override 
    public long currentTimeMS() { 
     return currentTimeMS; 
    } 

    @Override 
    public void currentTimeMS(long currentTimeMS) { 
     this.currentTimeMS =    currentTimeMS; 
    } 
} 

のようなものを使用します入力/イベントによって設定されます。プロダクションでは、VanillaTimeSource.INSTANCEを使用して入力/イベントの時刻を無視し、現在の時刻を使用します。

+0

最初の提案はおそらく勝ったでしょう仕事はありません。また、正確に何が取り出されたのかは、日付に基づいている(特定の列に従う)ことにも言及すべきである。つまり、ロジックが異なるわけではないにしても、毎年ほとんど毎日異なる結果を生成する必要があります。私はおそらく2番目の提案のために行く – Alex

+0

結果を確認する前に、テストの実行の一部として期待される結果を生成することができます。事前にそれらを生成する必要はありません。 –

+0

実際、時刻をパラメータとして受け取るクラス/コンポーネントを作成することは、システム時刻から切り離すので、それが良いコンポーネントであることを保証します。すべてのアプリケーション固有の構成(「システム時間の使用」)をコンポーネント外に置きます。外部構成可能な刺激にのみ依存する必要があります。 – helios

8

時間が表示される方法をカスタマイズできるように、クラスに何かを注入する必要があります。あなたは時間プロバイダの偽の実装を提供することができ、あなたのユニットテストで今すぐ

public interface TimeProvider { 
    DateTime getCurrentTime(); 
} 

public class UnderTest { 

    // Inject this in some way (e.g. provide it in the constructor) 
    private TimeProvider timeProvider; 

    public void MyMethod() { 
    if (timeProvider.getCurrentTime() == "1234") { 
     // Do something 
    } 
    } 
} 

例えば

。実際の生産コードでは、現在の日付時刻を返すことができます。

+0

もちろん、 'belgther'が示唆しているようにパラメータとして提供することもできます:) –

2

最近私はリファクタリングできなかったコードで同様の問題を抱えていました(時間制約があり、何かを間違って壊したくない)。それはSystem.currentTimeMillis()を呼び出してテストしたいメソッドを持っていました。テストしたいケースはその値が何を返すかによって異なります。ような何か:ユニットテストを許可するように

public class ClassINeedToTest { 
    public boolean doStuff() { 
     long l = System.currentTimeMillis(); 
     // do some calculation based on l 
     // and return the calculation 
    } 
} 

それは

protected long getCurrentTimeMillis() { 
    // only for unit-testing purposes 
    return System.currentTimeMillis(); 
} 

を保護し、この方法は、doStuff(で呼ばれたヘルパーメソッドを持っていたので、私は)クラスをリファクタリング。これは、機能を変更しかし、今、私はユニットテストでそれを呼び出すとき

ClassINeedToTest testClass = new ClassINeedToTest() { 
    protected long getCurrentTimeMillis() { 
     // return specific date for my test 
     return 12456778L; 
    } 
}; 
boolean result = testClass.doStuff(); 
// test result with an assert here 

しかしこれは私が汚染されていることを意味しているように、私は、その後、特定の値を返すためにこのメソッドをオーバーライドできることを意味しませんでした私のクラスのインタフェースでは、コストが高すぎると判断する可能性があります。あなたがコードをもっとリファクタリングできるなら、おそらくより良い方法があります。

関連する問題