2017-02-10 4 views
2

Ich bin derzeit ratlos mit Unit-Test eine Methode, die einen REST-Service innerhalb es für die Übermittlung eines Status an diesen Service ruft.Unit-Test für Rest Anruf

Ich habe eine Methode wie:

public void informService(String state, BusinessObject bus) { 

    Client client = null; 
    Response response = null; 
    try { 

    client = new ResteasyClientBuilder() 
          .establishConnectionTimeout(10L, TimeUnit.MINUTES) 
          .socketTimeout(10L, TimeUnit.MINUTES) 
          .build(); 

     WebTarget target = client.target("my.url"); 

     MyDTO dto = new MyDTO(); 
     dto.state = state; 

     response = target.request("application/json").put(Entity.json(dto)); 

     boolean ok = handleReturnCode(response.getStatus(), bus.getId()); 

     if (!ok) { 
     throw new RuntimeException("Invalid status" + response.getStatus()); 
     } 

     return ok; 

    } catch (Exception e) { 
    } finally { 
    try { 
     if (response != null) { 
     response.close(); 
     } 
     if (client != null) 
     client.close(); 
    } catch (Exception e) { 
    } 
} 

So in dem Verfahren ein Anruf an einen REST-Dienst ist, die innerhalb der Methode ausgewertet wird. Wie kann ich dieses Verhalten testen?

+0

Das ist wirklich hässlich Code muss ich sagen. Um dies zu testen, können Sie sich PowerMock ansehen, es könnte Ihnen helfen –

+0

Die Methode folgt nicht den SOLID-Prinzipien. Teilen Sie den Code in Responsabilities. Erstellen Sie den Client nicht in der Methode, empfangen Sie ihn nicht als Parameter oder verwenden Sie keine Factory. So können Sie sich über die Methode lustig machen und nur die interne Logik testen. Komponententests befassen sich mit der Testmethode/Klassenlogik, nicht mit anderen Klassen. –

Antwort

1

Wenn UnitTesting gegen einen Netzwerkdienst immer den tatsächlichen Netzwerkaufruf in einen anderen Class verschieben und hinter einem Interface verstecken.

Dann können Sie ein falsches Objekt für diese Schnittstelle erstellen.
In Ihrem Test injizieren Sie dann den gefälschten Dienst anstelle des echten.

//Interface for your service 
public interface MyService{ 
    void sendJson(JsonObject json, Callback callback); 

    interface Callback{ 
     void onResult(int resultCode); 
    } 
} 

//Class you want to test 
public void MyClass{ 
    private MyService myService; 

    public MyClass(MyService myService){ 
     this.myService = myService; 
    } 

    public void informService(String state, BusinessObject bus) { 
     //Do the network call with your service and handle the response 
    } 

} 

Jetzt in Ihrem Test können Sie eine FakeService erstreckt MyService erstellen und zurückgeben, was Antwortcode den Sie testen möchten.
Natürlich können Sie auch ein spöttisches Framework wie Mockito verwenden, um Ihre Fälschungen zu erstellen, als Sie sie nicht selbst schreiben müssen.

+0

Ich mag den Ansatz, aber ich a) kann den vorhandenen Code nicht zu sehr ändern und b) Ich denke, das ist eher ein Integrationstest. –

+0

@SarahM das ändert nichts an der Tatsache, dass die Frage, die Sie gestellt haben, über Komponententests, nicht über Integrationstests, und es gibt eine Welt der Unterschied zwischen diesen beiden Arten von Test. Es ist gegen die Aufstellungsortregeln, um eine Antwort bekannt zu geben, die die gestellte Frage nicht beantwortet. – Gimby

+0

Entschuldigen Sie, dass ich vage bin. Ich wollte sagen, dass die vorgeschlagene Antwort mehr in Richtung Integrationstests geht, während ich mehr nach einem Komponententest frage. –

0

sagte ich über die Macht mock so ich dir schreiben, was ich geschrieben habe:

Lassen Sie uns sagen, dies ist Ihre Methode:

public boolean informService(String state) { 

     Client client = null; 
     client = new ClientBuilder().build(true); 

     boolean ok = client.getOK(); 

     if (!ok) { 
      throw new RuntimeException("Invalid status"); 
     } 

     return ok; 

} 

Es ist leicht Version Ihrer Methode, aber wir noch schaffen neuer Builder hier, Klasse heißt TestRest. Für die Prüfung dieses eindeutig schlecht geschrieben Stück Code müssen wir PowerMock verwenden:

@RunWith(PowerMockRunner.class) //clear 
@PrepareForTest(TestRest.class) //clear 
public class TestRestTest { 

    @Test 
    public void shouldReturnOKWhenOK() throws Exception{ 

     ClientBuilder builder = PowerMock.createMock(ClientBuilder.class); //prepare ClientBuilder mock to inject while test 

     TestRest tested = new TestRest(); //tested class, of course 

     PowerMock.expectNew(ClientBuilder.class).andReturn(builder); //What we expect when certain class will be instantiated using new, like in our example Builder 

     EasyMock.expect(builder.build(true)).andReturn(new Client(true)); //What we want to obtain, of course you will return some mocked object i think, especially that this is Unit test and you will want to test logic of method, not other classes 

     PowerMock.replay(builder, ClientBuilder.class); // change to replay mode to get configured expectation 

     assertTrue(tested.informService("state")); //clear 

     PowerMock.verify(builder, ClientBuilder.class); 
    } 

} 

Auch hier Sie Maven Abhängigkeiten:

<dependency> 
     <groupId>org.powermock</groupId> 
     <artifactId>powermock-easymock-release-full</artifactId> 
     <version>1.6.4</version> 
    </dependency> 
    <dependency> 
     <groupId>org.easymock</groupId> 
     <artifactId>easymock</artifactId> 
     <version>3.4</version> 
    </dependency> 
    <dependency> 
     <groupId>org.mockito</groupId> 
     <artifactId>mockito-all</artifactId> 
     <version>1.10.8</version> 
    </dependency>