2017-01-13 3 views
1
verschieben

Ich versuche zu lernen, testbaren Code in Java schreiben. Ich habe viele Links gesehen, die besagen, dass eine Singleton-Klasse nicht gut implementiert werden kann, wenn ich Komponententests durchführen möchte. Also hier ist, was ich versuche zu lösen. Ich habe eine Klasse, die HTTP-Anfragen (HTTPRequest-Klasse) sendet. Ich möchte, dass diese Klasse sich Gedanken darüber macht, welches Protokoll verwendet werden soll (TLSv1.2 usw.). Ich habe mehrere andere Klassen, die nur HTTPRequest Klasse mit Körper, URL, etc.Wie in Richtung Abhängigkeitsinjektion mit Singleton-Klasse

Die Art, wie ich es ankam, war, dass HTTPRequest eine Singleton-Klasse sein kann, da es nicht notwendig ist, mehrere Instanzen der gleichen Sache zu erstellen. Das Protokoll für das gesamte Projekt ist identisch und muss nur einmal festgelegt werden.

So sieht meine HTTPRequest aus.

public class HTTPRequest { 
    private CloseableHttpClient httpClient; 
    private HttpGet httpGet; 
    private HttpPut httpPut; 
    private HttpPost httpPost; 

    HTTPRequest(CloseableHttpClient a, HttpGet b, HttpPut c, HttpPost d) { 
     httpClient = a; 
     httpGet = b; 
     httpPut = c; 
     httpPost = d; 
    } 

    public HTTPResponse getRequest(url) { 
     //Do some processing with httpClient & httpGet to send the request 
    } 
} 

nun aus den Klassen, die Httprequest benötigen, ist es eine Möglichkeit, getRequest zu rufen, sondern nur ein Objekt mit? Ich habe etwas über die Abhängigkeitsinjektion gelesen, aber ich bin nicht wirklich in der Lage herauszufinden, wie ich es machen soll.

Wie soll ich den Code so ändern, dass er auch testbar ist?

class SomeOtherClass { 

    private HTTPRequest httpRequest; 
    public void setHttpRequest(HTTPRequest httpRequest){ 

    } 

    public void someAction(){ 
     httpRequest.getRequest(...); 
    } 
} 

... 
SomeOtherClass someOtherClass = new SomeOtherClass(httpRequest); 

In dem obigen Beispiel HttpRequest (die Abhängigkeit) in SomeOtherClass injiziert:

+0

Warum übergeben Sie 'HttpGet',' HttpPut' usw. als Parameter im Konstruktor? – alayor

+0

@alayor Ich möchte die Antworten für HttpGet verspotten. Da ich es eigentlich nicht meinen Server möchte. – user1692342

+0

Aber könnten Sie 'HttpGet' in' getRequest' Methode übergeben? – alayor

Antwort

1

Was können Sie tun, ist der Konstruktor in HTTPRequest Klasse entfernen, und dann protected Methoden getHttpGet(), getHttpPut usw.

public class HTTPRequest { 

    public HTTPResponse getRequest(url) { 
     //Do some processing with httpClient & httpGet to send the request 
     getHttpGet(url).call(); 
    } 

    protected CloseableHttpClient getHttpClient() { 
     return new CloseableHttpClient(); 
    } 

    protected HttpGet getHttpGet(String url) { 
     return new HttpGet(url); 
    } 

    protected HttpPost getHttpPost(String url) { 
     return new HttpPost(url); 
    } 

    ... 
} 

Dann rief erstellen, können Sie eine HttpRequestMock Klasse erstellen, die von HttpRequest und Überschreibung erbt getHttp* Methoden.

public class HTTPRequestMock extends HttpRequest { 

    @Override 
    protected CloseableHttpClient getHttpClient() { 
     //return mock 
     return new CloseableHttpClientMock(); 
    } 

    @Override 
    protected HttpGet getHttpGet(String url) { 
     //return mock 
     return new HttpGetMock(url); 
    } 

    @Override 
    protected HttpPost getHttpPost(String url) { 
     //return mock 
     return new HttpPostMock(url); 
    } 

    ... 
} 

Last, können Sie HttpRequest oder HttpRequestMock entsprechend in die Komponententests injizieren.

class HTTPRequestClient { 

    private HTTPRequest httpRequest = HttpRequestFactory.getHttpRequest(); 

    public void callServer(){ 
     httpRequest.getRequest("http://someurl.com/"); 
    } 

    void setHttpRequest(HTTPRequest httpRequest){ 
     this.httpRequest = httpRequest; 
    } 
    } 

class HTTPRequestFactory { 
    private static final HttpRequest httpRequest = new HttpRequest(); 

    public static HTTPRequest getHttpRequest() { 
    return httpRequest; 
    } 
} 

class HTTPRequestClientTest { 
    HTTPRequestClient httpRequestClient; 

     @Before 
     public void setUp(){ 
      httpRequestClient = new HTTPRequestClient(); 
      httpRequestClient.setHttpRequest(new HttpRequestMock()); 
     } 

     @Test 
     ... 
     } 
+0

Danke für die detaillierte Erklärung. Ich werde jedoch mehrere HTTPRequestClients haben, die verschiedene Dinge tun. Ich sehe nicht die Notwendigkeit, mehrere Objekte für HTTPRequest für jede Klasse zu erstellen. – user1692342

+0

Sie können eine 'HTTPRequestFactory' erstellen. Und Sie können die Erstellung von nur einem "HTTPRequest" -Objekt steuern. – alayor

+0

Macht viel Sinn. Ich werde es ausprobieren. Danke für Ihre Hilfe :) – user1692342

0

Eine einfache Implementierung von Dependency Injection (ohne Injektions Framework) können unter Verwendung von setters erfolgen. Auf diese Weise kann ein Komponententest einen Mock von HttpRequest erstellen, ihn SomeOtherClass bereitstellen und SomeOtherClass ohne seine externen Abhängigkeiten testen.

Macht das Sinn?


Nun über Singletons: Herstellung von Httprequest ein Singleton macht es schwieriger zu Mock (Mit Mock Frameworks wie Mockito). Außerdem können Klassen wie SomeOtherClass einfach HttpRequest.getInstance() ausführen, so dass Ihr Test nichts dagegen tun kann.

+0

Ja, ich dachte darüber nach. Aber ich möchte nicht, dass HttpRequest von der Person, die SomeOtherClass aufruft, erstellt wird. Wie gehe ich in diesem Fall damit um? – user1692342

+0

Dann können Sie ein Injection-Framework wie Spring oder Guice verwenden. Das Framework instanziiert die Objekte und injiziert sie in Ihre Beans. – alexbt

+0

Ich schaute auf die Beispiele von Guice. Also in welchem ​​Fall sollte die Bindung erfolgen? Wird eine andere Klasse die Bindung machen? Wäre es möglich, dass du ein Beispiel gibst? – user1692342

0
  • Legen Sie eine leere Datei beans.xml in das WEB-INF Verzeichnis mit dem Namen.
  • Beschriften Sie Ihre HttpRequest Klasse mit @ApplicationScoped.
  • Fügen Sie allen Klassen, die es verwenden, ein lokales Feld hinzu und notieren Sie sie mit @Inject.
Verwandte Themen