J'essaie de se moquer de l'interface Apache HttpClient afin de se moquer de l'une de ses méthodes mentionnées ci-dessous pour renvoyer un objet JSON tronqué en réponse.
HttpResponse response = defaultHttpClient.execute(postRequest);
Quelqu'un pourrait-il suggérer comment y parvenir avec un exemple de code? Votre aide serait grandement appréciée.
Merci
Voici ce que j'ai fait pour tester mon code à l'aide de Mockito et Apache HttpBuilder:
Classe sous test:
import Java.io.BufferedReader;
import Java.io.IOException;
import javax.ws.rs.core.Response.Status;
import org.Apache.http.HttpResponse;
import org.Apache.http.client.HttpClient;
import org.Apache.http.client.methods.HttpGet;
import org.Apache.http.impl.client.HttpClientBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class StatusApiClient {
private static final Logger LOG = LoggerFactory.getLogger(StatusApiClient.class);
private String targetUrl = "";
private HttpClient client = null;
HttpGet httpGet = null;
public StatusApiClient(HttpClient client, HttpGet httpGet) {
this.client = client;
this.httpGet = httpGet;
}
public StatusApiClient(String targetUrl) {
this.targetUrl = targetUrl;
this.client = HttpClientBuilder.create().build();
this.httpGet = new HttpGet(targetUrl);
}
public boolean getStatus() {
BufferedReader rd = null;
boolean status = false;
try{
LOG.debug("Requesting status: " + targetUrl);
HttpResponse response = client.execute(httpGet);
if(response.getStatusLine().getStatusCode() == Status.OK.getStatusCode()) {
LOG.debug("Is online.");
status = true;
}
} catch(Exception e) {
LOG.error("Error getting the status", e);
} finally {
if (rd != null) {
try{
rd.close();
} catch (IOException ioe) {
LOG.error("Error while closing the Buffered Reader used for reading the status", ioe);
}
}
}
return status;
}
}
Test:
import Java.io.IOException;
import org.Apache.http.HttpResponse;
import org.Apache.http.StatusLine;
import org.Apache.http.client.ClientProtocolException;
import org.Apache.http.client.HttpClient;
import org.Apache.http.client.methods.HttpGet;
import org.Apache.http.conn.HttpHostConnectException;
import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;
public class StatusApiClientTest extends Mockito {
@Test
public void should_return_true_if_the_status_api_works_properly() throws ClientProtocolException, IOException {
//given:
HttpClient httpClient = mock(HttpClient.class);
HttpGet httpGet = mock(HttpGet.class);
HttpResponse httpResponse = mock(HttpResponse.class);
StatusLine statusLine = mock(StatusLine.class);
//and:
when(statusLine.getStatusCode()).thenReturn(200);
when(httpResponse.getStatusLine()).thenReturn(statusLine);
when(httpClient.execute(httpGet)).thenReturn(httpResponse);
//and:
StatusApiClient client = new StatusApiClient(httpClient, httpGet);
//when:
boolean status = client.getStatus();
//then:
Assert.assertTrue(status);
}
@Test
public void should_return_false_if_status_api_do_not_respond() throws ClientProtocolException, IOException {
//given:
HttpClient httpClient = mock(HttpClient.class);
HttpGet httpGet = mock(HttpGet.class);
HttpResponse httpResponse = mock(HttpResponse.class);
StatusLine statusLine = mock(StatusLine.class);
//and:
when(httpClient.execute(httpGet)).thenThrow(HttpHostConnectException.class);
//and:
StatusApiClient client = new StatusApiClient(httpClient, httpGet);
//when:
boolean status = client.getStatus();
//then:
Assert.assertFalse(status);
}
}
Que pensez-vous les gens, dois-je améliorer quelque chose? (Ouais, je sais, les commentaires. C'est quelque chose que j'ai apporté de mon expérience Spock: D)
Dans votre classe de test unitaire, vous devez vous moquer de defaultHttpClient
:
@Mock
private HttpClient defaultHttpClient;
Ensuite, vous dites à mockito (par exemple dans la méthode @Before
) De créer réellement vos mocks en:
MockitoAnnotations.initMocks(YourTestClass);
Ensuite, dans votre méthode de test, vous définissez quelle méthode execute()
doit renvoyer:
when(defaultHttpClient.execute(any()/* or wahtever you want here */)).thenReturn(stubbed JSON object);
Vous pouvez regarder HttpClientMock , je l'ai écrit pour un projet interne mais j'ai décidé plus tard d'ouvrir la source. Il vous permet de définir un comportement simulé avec une API fluide et de vérifier ultérieurement un certain nombre d'appels effectués. Exemple:
HttpClientMock httpClientMock = new
HttpClientMock("http://localhost:8080");
httpClientMock.onGet("/login?user=john").doReturnJSON("{permission:1}");
httpClientMock.verify().get("/login?user=john").called();
Vous pouvez le faire facilement en utilisant PowerMockito qui peut également se moquer facilement des méthodes finales/statiques, des méthodes privées et des classes anonymes. Voici l'exemple de code pour se moquer de la requête http. JSON_STRING_DATA est une chaîne que vous souhaitez obtenir de la méthode d'exécution.
PowerMockito.mockStatic(DefaultHttpClient.class);
HttpClient defaultHttpClientMocked = PowerMockito.mock(DefaultHttpClient.class);
PowerMockito.when(defaultHttpClientMocked.execute(Mockito.any(HttpPost.class))).thenReturn(createMockedHTTPResponse(JSON_STRING_DATA));
Il existe une meilleure façon de le faire sans avoir à ajouter PowerMock comme une autre dépendance. Ici, vous n'avez besoin que d'un constructeur supplémentaire prenant HTTPClient comme argument et Mockito. Dans cet exemple, je crée un bilan de santé personnalisé (Spring Actuator) et je dois me moquer de HTTPClient pour les tests unitaires.
Libs: JUnit 5, Spring Boot 2.1.2 et Mockito 2.
Composant:
@Component
public class MyHealthCheck extends AbstractHealthIndicator {
HttpClient httpClient;
public MyHealthCheck() {
httpClient = HttpClientBuilder.create().build();
}
/**
Added another constructor to the class with an HttpClient argument.
This one can be used for testing
*/
public MyHealthCheck(HttpClient httpClient) {
this.httpClient = httpClient;
}
/**
Method to test
*/
@Override
protected void doHealthCheck(Builder builder) throws Exception {
//
// Execute request and get status code
HttpGet request = new HttpGet("http://www.SomeAuthEndpoint.com");
HttpResponse response = httpClient.execute(request);
//
// Update builder according to status code
int statusCode = response.getStatusLine().getStatusCode();
if(statusCode == 200 || statusCode == 401) {
builder.up().withDetail("Code from service", statusCode);
} else {
builder.unknown().withDetail("Code from service", statusCode);
}
}
}
Méthode d'essai:
Notez que nous utilisons ici Mockito.any (HttpGet.class)
private static HttpClient httpClient;
private static HttpResponse httpResponse;
private static StatusLine statusLine;
@BeforeAll
public static void init() {
//
// Given
httpClient = Mockito.mock(HttpClient.class);
httpResponse = Mockito.mock(HttpResponse.class);
statusLine = Mockito.mock(StatusLine.class);
}
@Test
public void doHealthCheck_endReturns401_shouldReturnUp() throws Exception {
//
// When
when(statusLine.getStatusCode()).thenReturn(401);
when(httpResponse.getStatusLine()).thenReturn(statusLine);
when(httpClient.execute(Mockito.any(HttpGet.class))).thenReturn(httpResponse);
//
// Then
MyHealthCheck myHealthCheck = new MyHealthCheck(httpClient);
Health.Builder builder = new Health.Builder();
myHealthCheck.doHealthCheck(builder);
Status status = builder.build().getStatus();
Assertions.assertTrue(Status.UP == status);
}