2016-10-12 32 views
2

Ich möchte eine Map erstellen, die Strings als Schlüssel enthält und Instanzen der Klasse Candidate als Werte spottet.JMockit kann nicht mehr als eine Instanz der Klasse vortäuschen

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); 
    } 

Der obige Code verwendet zu arbeiten, bevor JMockit 1,20-1,28 aktualisieren.

Jetzt bekomme ich eine Ausnahme:

java.lang.IllegalStateException: Ungültige Versuch nicht initialisierte Instanz der Klasse com.urlservice.data.Candidate von staatenlos Mockup auf zu bekommen ...

ich die Dokumentation zu lesen und versuchte new MockUp(T targetInstance) stattdessen in der folgenden Art und Weise zu verwenden (dies ist die körpereigene Schleife):

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; } 
}; 

Das Ergebnis war sehr seltsam - der erste Kandidat wurde richtig verspottet, während der Rest der verhöhnten Kandidaten überhaupt nicht verspottet wurde und ihre wahren Methoden genannt wurden.

Ich habe versucht, zurück in die Expectations API zurückzukehren:

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; 
}}; 

ohne Erfolg:

java.lang.IllegalArgumentException: Klasse com.urlservice.data.Candidate bei: Bereits verspottet ...

Ich möchte wirklich auf die neueste Version aktualisieren, aber ich kann keine Problemumgehung für dieses Problem finden.

UPDATE: Ich habe es nicht geschafft, dieses Problem in irgendeiner Version bis zu 1.28 zu reproduzieren, also denke ich, das ist die Version, in der es eingeführt wurde.

Zusätzlich zu meinem zweiten Beispiel (new MockUp(T targetInstance)), sah ich den Quellcode der Klasse MockUp Linie 402 und es sieht für mich wie das erwartete Verhalten ist, keine spezifische Zielinstanz außer dem ersten zu verspotten:

MockUp<?> previousMockUp = findPreviouslyFakedClassIfMockUpAlreadyApplied(); 

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

Was fehlt mir?

UPDATE2: Ich kam mit einem fehlerhaften Testbeispiel. Es ist ein bisschen umständlich, aber ich bin sicher, dass es den Punkt durchkommen wird.

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 
} 
} 

Bitte beachten Sie, dass selbst wenn dieses Beispiel nicht gescheitert wäre, die Tatsache, dass ich, bevor es zu MockUp ‚s Konstruktor meine Abhängigkeit instanziiert muß vorbei ist bestenfalls problematisch. Ist das nicht der Sinn eines Mocks, dass man es nicht instanziieren muss?

+0

Ich konnte das "seltsame" Ergebnis nicht reproduzieren, wenn ich 'new MockUp (cand) 'verwende. Es scheint mit 1,28 gut zu funktionieren. Können Sie einen fehlerhaften Beispieltest zeigen? –

+0

Beachten Sie die [API-Dokumentation] (http://jmockit.org/api1x/mockit/MockUp.html#MockUp-T-) für 'Mock (T)' sagt "betrifft nur die angegebene Instanz". So werden Methoden, die an irgendwelchen * anderen * Instanzen von 'Kandidat' aufgerufen werden, nicht zur '@ Mock'-Methode gehen. –

+0

@ Rogério Genau das habe ich natürlich versucht. Ich werde versuchen, einen fehlerhaften Beispieltest so schnell wie möglich zur Verfügung zu stellen, aber stellt das JMockit-Codebeispiel, das ich am Ende gestellt habe, meinen Anspruch nicht stark zurück? – KidCrippler

Antwort

0

Nach eng JMockit der eigentlichen Projekt Unit-Tests untersucht (die sind sehr klar und organisiert), schaffte ich es, um das Problem in einer etwas eigentümlichen Art und Weise gelöst:

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()); 
    } 
} 

Auch wenn meine Tests jetzt passieren, I don‘ Verstehen Sie vollständig, warum die einzige Möglichkeit, eine bestimmte Instanz einer Klasse zu verspotten, darin besteht, eine andere Ad-hoc-Klasse zu erstellen (im Gegensatz zu einer anonymisierten Einfügung, wie ich es vor dieser Version gewohnt war).

Dieser Ansatz benötigt ein paar Zeilen mehr (private Felddeklaration, ctor-Implementierung), ist aber wohl eleganter.

Es wäre immer noch sehr zu begrüßen, wenn einer von JMockits Mitwirkenden etwas Licht in diese Angelegenheit bringen könnte.

+0

Eigentlich ist das nicht der einzige Weg (siehe die Klasse 'Aufruf'). Und es ist nicht notwendig, ein benanntes Modell wie oben zu erstellen; es muss nur ein "stateful" Mockup sein (mit Instanzfeldern), das immer noch mit einer anonymen Unterklasse geschrieben werden kann. Das eigentliche Problem besteht darin, dieselbe Mockup-Klasse mehrfach mit verschiedenen verknüpften Mock-Instanzen zu verwenden. Dies wird nicht unterstützt, da JMockit eine interne Map von mokierten Instanzen zu Mockup-Instanzen verwalten muss. Was technisch machbar ist, aber nicht zur beabsichtigten Nutzung der API passt. –

+0

Also im Grunde, was ich hier getan habe, ist die beabsichtigte Verwendung der API mit einem benannten Modell umgehen? Können Sie bitte erläutern, warum dies nicht beabsichtigt ist? – KidCrippler

+0

Mock-ups sollen * Fake * -Implementierungen (meistens von Klassen) liefern, nicht als eine bequeme Art, "Daten" -Objekte mit wenig oder keinem komplexen/teuren Verhalten zu erzeugen. Der Test sollte stattdessen die Klasse mit dem gewünschten Status in so vielen Instanzen instanziieren, wie sie benötigt wird. Normalerweise keine Verspottung oder Vortäuschung dieser Instanzen. –

Verwandte Themen