2016-07-21 13 views
2

Ich möchte eine Autowired-Klasse eines Diensts in meiner Spring-Start-App durch eine mokierte Implementierung dieser Klasse ersetzen, die ich speziell zum Testen erstellt habe.Verwenden von @InjectMocks zum Ersetzen von @Autowired-Feld durch eine mokierte Implementierung

Ich habe mich dafür entschieden, diese mockige Implementierung zu erstellen, weil das Verhalten dieser Klasse zu kompliziert ist, um Mockito zu verspotten, da es mehrere andere Mocks selbst benötigt.

Ich bin nicht in der Lage herauszufinden, wie diese gespottete Implementierung in den Dienst injiziert werden.

Hier ist ein minimales Beispiel für die Situation:

@Service 
public class ServiceIWantToTestImpl implements ServiceIWantToTest{ 

    @Autowired 
    ComplicatedDependency complicatedDependency; 

    @Override 
    public void methodUsingDependency(){ 
     String string = complicatedDependency.doSomething(); 
     System.out.println(string); 
    } 

} 


public class MockComplicatedDependency implements ComplicatedDepencency{ 

    public MockComplicatedDependency(...){ 
     // Inject other mocked objects into this mock 
    } 

    public String doSomthing(){ 
     // This would be a mocked version of this function for testing 
     return "test"; 
    } 

} 

@RunWith(MockitoJUnitRunner.class) 
public class TestingTheService(){ 
    @InjectMock 
    private static ServiceIWantToTest serviceIWantToTest = new ServiceIWantToTestImpl(); 

    @Mock 
    ComplicatedDependency mockComplicatedDependency; 

    @BeforeClass 
    public static void init(){ 
     mockComplicatedDependency = new MockComplicatedDependency(...); 
    } 

    @Test 
    public void testAttempt(){ 
     serviceIWantToTest.methodUsingDependency(); // This method calls complicatedDependency.doSomething() which does not run the mocked version in MockComplicatedDependency which I wanted to inject, and would always return null instead of the "test" string I put in this example. 
    } 

} 

Antwort

4

Haben Sie Mockito Anmerkungen Setup Abhängigkeiten für die Klasse im Test verwenden?

Wenn das nicht das wichtigste Hindernis ist warum nicht nur die Ebene einfache Einrichtung tun und einen Konstruktor oder einen Setter in ServiceIWantToTestImpl Klasse für das ComplicatedDependency Feld einzuführen und die Abhängigkeit im Testaufbau gesetzt direkt auf was auch immer impl von ComplicatedDependency Sie wie zB:

@Service 
public class ServiceIWantToTestImpl implements ServiceIWantToTest { 

    @Autowired 
    ComplicatedDependency complicatedDependency; 

    public ServiceIWantToTestImpl() { 

    } 

    public ServiceIWantToTestImpl(ComplicatedDependency complicatedDependency) { 
     this.complicatedDependency = complicatedDependency; 
    } 

    @Override 
    public void methodUsingDependency(){ 
     String string = complicatedDependency.doSomething(); 
     System.out.println(string); 
    } 
} 

public class TestingTheService { 

    private static ServiceIWantToTestImpl serviceIWantToTest; 

    @BeforeClass 
    public static void init(){ 
     serviceIWantToTest = new ServiceIWantToTestImpl(new MockComplicatedDependency()); 
    } 

    @Test 
    public void testAttempt() { 
     serviceIWantToTest.methodUsingDependency(); 
    } 

} 

Das ist ein Weg.

Um es mit Mockito funktioniert, Sie konnte @Spy zu verwenden, anstatt @Mock wie folgt aus:

@RunWith(MockitoJUnitRunner.class) 
public class TestingTheService { 

    @InjectMocks 
    private static ServiceIWantToTestImpl serviceIWantToTest = new ServiceIWantToTestImpl(); 

    @Spy 
    private static ComplicatedDependency complicatedDependency = new MockComplicatedDependency(); 

    @BeforeClass 
    public static void init() { 

    } 

    @Test 
    public void testAttempt() { 
     serviceIWantToTest.methodUsingDependency(); 
    } 

} 

Obwohl dies ein bisschen wie ein Hack ist. Ich empfehle dringend, dass Sie den JavaDoc der @Spy-Annotation lesen und sicherstellen, dass die erwartete Verwendung für Ihren Test wirklich erforderlich ist.

+0

Ich habe versucht mit @Spy wie Sie vorgeschlagen und es funktioniert super. Vielen Dank! Ich möchte nicht wirklich einen Konstruktor nur zum Testen einführen, ich möchte Spring die Dependency-Injektion in der Anwendung behandeln lassen. –

+2

Eigentlich ist die Constructor-Level-Injektion der empfohlene Ansatz, Feld-Level-Injektion wird im Allgemeinen abgeraten –

+1

Wie schon von @Milos erwähnt, hat Spring schon seit einem Jahrzehnt die Konstruktor-Injektion empfohlen: http://spring.io/blog/2007/07/11/setter-injection-versus-constructor-injection-and-the-use-of-required /. Niemand sollte mehr Feldinjektionen verwenden. – jhyot