2016-10-12 53 views
2

キーとしてStringsを保持し、値としてクラスCandidateの模擬インスタンスを保持するマップを作成したいとします。JMockitがクラスの複数のインスタンスをモックできません

Map<String, Long> domainNameToId = new HashMap<String, Long>(); 
    domainNameToId.put("farmaciapuentezurita.es", 1234l); 
    domainNameToId.put("vivefarma.com", 2345l); 
    domainNameToId.put("eurofarmacia.com", 3456l); 

    Map<String, Candidate> expectedCandidates = new HashMap<String, Candidate>(); 
    for(String domain : domainNameToId.keySet()) { 
     final Candidate cand = new MockUp<Candidate>() { 
      @Mock Long getDomainId() { return domainNameToId.get(domain); } // private method 
      @Mock boolean validateAndPrepare() { return true; } 
      @Mock String getRepresentingName() { return domain; } 
     }.getMockInstance(); 
     expectedCandidates.put(domain, cand); 
    } 

1.28に1.20からJMockitをアップグレードする前に働いて上記のコード。

今、私は例外を取得:

java.lang.IllegalStateException:

...でステートレスなモックアップ からクラスcom.urlservice.data.Candidateの初期化されていないインスタンスを取得するための不正な試み

は私がマニュアルを読み、以下の方法で代わりにnew MockUp(T targetInstance)を使用しようとした(これは、ループの体である):

final Candidate cand = new Candidate(domain); 
new MockUp<Candidate>(cand) { 
    @Mock Long getDomainId() { return domainNameToId.get(domain); } // private method 
    @Mock boolean validateAndPrepare() { return true; } 
    @Mock String getRepresentingName() { return domain; } 
}; 

結果は非常に奇妙でした。最初の候補者は適切に嘲笑され、残りの嘲笑候補者はまったく嘲笑されず、実際の方法が呼び出されました。

java.lang.IllegalArgumentExceptionが::すでに嘲笑:クラスcom.urlservice.data.Candidate で無駄に

final Candidate cand = new Candidate(domain); 
new Expectations(cand) {{ 
    cand.getDomainId(); result = domainNameToId.get(domain); // Had to make it public :-(
    cand.validateAndPrepare(); result = true; 
    cand.getRepresentingName(); result = domain; 
}}; 

は、私は戻って Expectations APIに戻すことを試みました...

本当に最新バージョンにアップグレードしたいのですが、この問題の回避策が見つかりません。

更新日:この問題は、1.28までのバージョンで再現できませんでした。このバージョンが導入されたバージョンだと思います。

さらに、私の第2の例(new MockUp(T targetInstance))に関連して、私はクラスMockUp行402のソースコードを見て、期待される振る舞いが最初のもの以外の特定のターゲットインスタンスをモックしないように見えます。

MockUp<?> previousMockUp = findPreviouslyFakedClassIfMockUpAlreadyApplied(); 

    if (previousMockUp != null) { 
    targetType = previousMockUp.targetType; 
    mockedClass = previousMockUp.mockedClass; 
    return; // Input param targetInstance is disregarded 
    } 

私は何が欠けていますか?

UPDATE2:私は失敗したテストの例を思いつきました。ちょっと面倒ですが、私はそれがポイントを得るだろうと確信しています。

public class SampleTest { 

class TestedClass { 
    private IncrementingDependency dep; 
    TestedClass(IncrementingDependency dep) { this.dep = dep; } 
    public int getVal() { return dep.inc(); } 
} 

class IncrementingDependency { 
    int val; 
    public IncrementingDependency(int val) { this.val = val; } 
    public int inc() { return ++val; } 
} 

@Test 
public void sampleTest() { 
    List<Integer> inputVals = Arrays.asList(1, 2, 3); 
    List<TestedClass> incrementingClasses = new ArrayList<TestedClass>(); 

    for (Integer num : inputVals) { 
     IncrementingDependency dep = new IncrementingDependency(num); 
     new MockUp<IncrementingDependency>(dep) { 
      @Mock int inc() { return num; } // Mock with different behavior - DON'T INCREMENT 
     }; 
     incrementingClasses.add(new TestedClass(dep)); 
    } 

    assertThat(incrementingClasses.get(0).getVal()).isEqualTo(1); // Passes - 1 wasn't incremented (mocked behavior) 
    assertThat(incrementingClasses.get(1).getVal()).isEqualTo(2); // Fails - real code was called and 2 was incremented to 3 
    assertThat(incrementingClasses.get(2).getVal()).isEqualTo(3); // We never get to this point 
} 
} 

この例では、失敗していません場合でも、私はMockUpのコンストラクタに渡す前に、私の依存関係をインスタンス化する必要があるという事実は、せいぜい問題があることに注意してください。あなたがそれをインスタンス化する必要はないということですか?

+0

'new MockUp (cand)'を使用しているときに「奇妙な」結果を再現できませんでした。それは1.28でうまくいくようです。失敗したサンプルテストを表示できますか? –

+0

'Mock (T)'の[API documentation](http://jmockit.org/api1x/mockit/MockUp.html#MockUp-T-)には「指定されたインスタンスにのみ影響する」と記載されています。だから、 'Candidate'の* other *インスタンスに対して呼び出されたメソッドは' @ Mock'メソッドには行きません。 –

+0

@Rogérioもちろん、それは私が達成しようとしていたものです。私は失敗したサンプルテストをできるだけ早く提供しようとしますが、最後にJMockitのコードサンプルを書いて、私の主張を強くバックアップしていませんか? – KidCrippler

答えて

0

は密接に(非常に明確で編成されている)JMockitの実際のプロジェクトのユニットテストを調べた後、私はやや独特の方法で問題を解決するために管理:

private void expectCandidatesFromMap(final Map<String, Long> domainNameToId) { 
    Map<String, Candidate> expectedCandidates = new HashMap<String, Candidate>(); 

    class MockedCandidate extends MockUp<Candidate> { 
     private final String domainName; 
     private final Long domainId; 

     MockedCandidate(String domainName) { 
      this.domainName = domainName; 
      this.domainId = domainNameToId.get(domainName); 
     } 

     @Mock Long getDomainId() { return domainId; } 
     @Mock String getRepresentingName() { return domainName; } 
     @Mock boolean validateAndPrepare() { return true; } 
    } 

    for (String domain : domainNameToId.keySet()) { 
     expectedCandidates.put(domain, new MockedCandidate(domain).getMockInstance()); 
    } 
} 

私のテストは、今私はドン、合格していても」なぜ、クラスの特定のインスタンスをモックする唯一の方法が別のアドホッククラスを作成することであるのかを完全に理解しています(このバージョンの前に慣れていたように匿名でインライン展開するのではなく)。

このアプローチには、さらにいくつかの行(プライベートフィールド宣言、ctor実装)が必要ですが、間違いなくよりエレガントです。

JMockitの貢献者の1人がこの問題を明らかにすることができれば、それでもなお非常に感謝しています。

+0

これは唯一の方法ではありません( 'Invocation'クラスを参照してください)。そして、上記のような名前のモックアップを作成する必要はありません。匿名のサブクラスを使用して記述できる「ステートフル」モックアップ(インスタンスフィールドあり)のみである必要があります。実際の問題は、同じモックアップクラスを、関連するモックされた異なるインスタンスに複数回適用することです。これは、JMockitに模擬インスタンスからモックアップインスタンスへの内部マップを維持する必要があるため、サポートされていません。技術的には実行可能ですが、APIの意図された用途には適合しません。 –

+0

基本的に私がここでやったのは、名前付きモックアップでAPIの意図された使い方をバイパスすることです。それが意図された使用法ではない理由を詳しく教えてください。 – KidCrippler

+0

モックアップは、複雑で高価な動作をほとんどまたはまったく伴わない "データ"オブジェクトを作成する便利な方法ではなく、(クラスの)ほとんどの*偽の実装を提供するためのものです。テストでは、代わりに必要な数だけ必要な状態のクラスをインスタンス化する必要があります。前記のインスタンスの嘲笑や捏造は、通常はありません。 –

関連する問題