Testarea clientului REST folosind Instrumentul Restito: Ce este clientul REST?

Ce este REST?

REST รฎnseamnฤƒ โ€žTransfer de stat reprezentativโ€, care este o nouฤƒ modalitate de comunicare รฎntre oricare douฤƒ sisteme la un moment dat. Unul dintre sisteme se numeศ™te โ€žClient RESTโ€, iar celฤƒlalt se numeศ™te โ€žServer RESTโ€.

รŽnainte de a afla despre Cadrul Restito pentru testarea clienศ›ilor REST, mai รฎntรขi sฤƒ รฎnvฤƒศ›ฤƒm cรขteva elemente de bazฤƒ.

Ce este REST Client?

Clientul REST este o metodฤƒ sau un instrument pentru invocarea unui API de servicii REST expus pentru comunicare de cฤƒtre orice sistem sau furnizor de servicii. De exemplu: dacฤƒ un API este expus pentru a obศ›ine informaศ›ii despre trafic รฎn timp real despre o rutฤƒ de la Google, software-ul/instrumentul care invocฤƒ Google API-ul de trafic se numeศ™te client REST.

Ce este REST Server?

Este o metodฤƒ sau o interfaศ›ฤƒ de programare a aplicaศ›iilor (API) expusฤƒ comunicฤƒrii de cฤƒtre orice sistem sau furnizor de servicii. De exemplu, Google expune o API pentru a obศ›ine informaศ›ii despre trafic รฎn timp real pe o anumitฤƒ rutฤƒ.

Aici Google Serverul trebuie sฤƒ fie funcศ›ional pentru a asculta orice solicitฤƒri cฤƒtre API-ul expus de la diferiศ›i clienศ›i.

Exemplu:

Este timpul sฤƒ stabilim un scenariu complet End-to-End din definiศ›iile de mai sus.

Sฤƒ luฤƒm รฎn considerare aplicaศ›iile de rezervare a taxiului precum Uber, deoarece o companie are nevoie de informaศ›ii รฎn timp real despre situaศ›ia traficului รฎn jurul rutelor pe care se aflฤƒ un anumit vehicul.

Client de odihnฤƒ:

Aici, clientul este o aplicaศ›ie mobilฤƒ Uber la care ศ™oferul s-a conectat. Aceastฤƒ aplicaศ›ie trimite o solicitare cฤƒtre API-ul REST expus de Google hฤƒrศ›i pentru a obศ›ine date รฎn timp real. De exemplu, o cerere HTTP GET.

Server de odihnฤƒ:

รŽn acest exemplu, Google este furnizorul de servicii ศ™i Google API-ul hฤƒrศ›ilor rฤƒspunde cu detaliile necesare la solicitarea aplicaศ›iei Uber.

Atรขt clientul, cรขt ศ™i serverul sunt la fel de importante รฎn comunicarea REST.

Aici, am implementat exemple pentru testarea automatฤƒ numai a clientului REST. Pentru testarea serverului REST, consultaศ›i https://www.guru99.com/api-testing-tool.html.

Ce este Restito?

Restito este un cadru dezvoltat de Mkotsur. Este o aplicaศ›ie uศ™oarฤƒ care vฤƒ ajutฤƒ sฤƒ executaศ›i orice fel de solicitare HTTP. Puteศ›i folosi Restito pentru a vฤƒ testa API-urile REST ศ™i pentru a cฤƒuta probleme รฎn aplicaศ›ia dvs. sau รฎn reศ›ea.

Cum se testeazฤƒ clientul REST folosind Restito?

Sฤƒ รฎmpฤƒrศ›im exerciศ›iul รฎn urmฤƒtorii 4 paศ™i:

  1. Creaศ›i un client HTTP ศ™i o metodฤƒ pentru a trimite o solicitare HTTP GET cฤƒtre orice punct final al serverului. Pentru moment, luaศ›i รฎn considerare punctul final ca fiind http://localhost:9092/getevents.
  1. Porniศ›i un server Restito pentru a asculta ศ™i captura cererile trimise la punctul final โ€žgeteventsโ€ รฎn localhost http://localhost:9092/getevents.
  1. Creaศ›i o clasฤƒ de testare pentru a testa clientul de mai sus. Invocaศ›i metoda โ€žsendGETRequestโ€ a clientului HTTP pentru a iniศ›ia o solicitare GET cฤƒtre API โ€žgeteventsโ€.
  1. Validaศ›i apelul HTTP GET utilizรขnd cadrul Restito.

Sฤƒ ne aprofundฤƒm รฎn fiecare dintre paศ™ii de mai sus.

Pas 1) Creaศ›i un client HTTP ศ™i o metodฤƒ pentru a trimite cererea HTTP GET cฤƒtre orice punct final al serverului.

========== COD JAVA รŽncepe ===========

package com.chamlabs.restfulservices.client;

import java.util.HashMap;
import java.util.Map;

import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClientBuilder;
import org.json.JSONObject;

/**
 * This class creates a HTTP Client and has a method to send HTTP GET request: 
 * 		sendGETRequest(..)
 */
public class RestClient {
	/**
	 * Constructor for the  class RestClient	
	 */
	public RestClient() {
		System.out.println("Creating RestClient constructor");
	}

	/**
	 * Method to Send GET request to http://localhost:<<port>>/getevents
	 * @param port
	 * @return true if GET request is successfully sent. False, otherwise.
	 */
	public static boolean sendGETRequest(int port) {
		try {
			HttpClient client = HttpClientBuilder.create().build();
			HttpGet getRequest = new HttpGet("http://localhost:" + port + "/getevents");
			//HttpResponse response = client.execute(request);
			client.execute(getRequest);
			System.out.println("HTTP request is sent successfully."
					+ "Returning True");
			return true;
		} 
		
		catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println("Some exception has occurred during the HTTP Client creation."
				+ "Returning false");
		return false;
	}
}

========== COD JAVA se terminฤƒ ===========

Pas 2) Porniศ›i un server Restito pentru a asculta ศ™i captura cererile trimise la punctul final โ€žgeteventsโ€ รฎn localhost http://localhost:9092/getevents.

========== COD JAVA รŽncepe ===========

package com.chamlabs.restfultesting.util;

import static com.xebialabs.restito.builder.stub.StubHttp.whenHttp;
import static com.xebialabs.restito.semantics.Action.status;
import static com.xebialabs.restito.semantics.Condition.get;
import static com.xebialabs.restito.semantics.Condition.post;
import java.util.List;
import org.glassfish.grizzly.http.util.HttpStatus;
import com.xebialabs.restito.semantics.Call;
import com.xebialabs.restito.server.StubServer;

/**
 * This utility class contains several utility methods like : 
 * 		restartRestitoServerForGETRequests(..)
 * 		restartRestitoServerForPOSTRequests(..) 
 * 		waitAndGetCallList(..)
 * 
 * @author cham6
 * @email: paperplanes.chandra@gmail.com
 * @fork: https://github.com/cham6/restfultesting.git
 *
 */
public class TestUtil {
	
	/**
	 * Utility method to start restito stub server to accept GET requests
	 * @param server
	 * @param port
	 * @param status
	 */
	public static void restartRestitoServerForGETRequests (StubServer server, int port, HttpStatus status)
    {
        // Kill the restito server
        if (server != null) {
            server.stop();
        }
        // Initialize and configure a newer instance of the stub server
        server = new StubServer(port).run();
        whenHttp(server).match(get("/getevents")).then(status(status));
    }
	
	/**
	 * Utility method to start restito stub server to accept POST requests
	 * @param server
	 * @param port
	 * @param status
	 */
	public static void restartRestitoServerForPOSTRequests (StubServer server, int port, HttpStatus status)
    {
        // Kill the restito server
        if (server != null) {
            server.stop();
        }
        // Initialize and configure a newer instance of the stub server
        server = new StubServer(port).run();
        whenHttp(server).match(post("/postevents")).then(status(status));
    }
	
	/**
     * For a given restito stub server, loop for the given amount of seconds and
     * break and return the call list from server.
     * 
     * @param server
     * @param waitTimeInSeconds
     * @return
     * @throws InterruptedException
     */
	public static List<Call> waitAndGetCallList (StubServer server, int waitTimeInSeconds)
        throws InterruptedException
    {
        int timeoutCount = 0;
        List<Call> callList = server.getCalls();
        while (callList.isEmpty()) {
            Thread.sleep(1000);
            timeoutCount++;
            if (timeoutCount >= waitTimeInSeconds) {
                break;
            }
            callList = server.getCalls();
        }
        // Wait for 2 seconds to get all the calls into callList to Eliminate any falkyness.
        Thread.sleep(2000);
        return server.getCalls();
    }


}

========== COD JAVA se terminฤƒ ===========

Pas 3) Creaศ›i o clasฤƒ de testare pentru a testa clientul de mai sus. Invocaศ›i metoda client HTTP sendGETRequest pentru a iniศ›ia o solicitare GET cฤƒtre API โ€žgeteventsโ€.

========== COD JAVA รŽncepe ===========

import junit.framework.TestCase;

import com.chamlabs.restfulservices.client.RestClient;
import com.chamlabs.restfultesting.util.TestUtil;
import com.xebialabs.restito.semantics.Call;
import com.xebialabs.restito.server.StubServer;
import static org.glassfish.grizzly.http.util.HttpStatus.ACCEPTED_202;
import org.json.JSONObject;
import java.util.List;
import java.util.Map;

/**
 * This class contains several junit tests to validate the RestClient operations like: 
 * 		sendRequest(..)
 * 		sendRequestWithCustomHeaders(..) 
 * 		sendPOSTRequestWithJSONBody(..)
 *
 */

public class RestClientTester extends TestCase {
	
    private static final Integer PORT = 9098;
    private static final Integer PORT2 = 9099;
    private static final Integer PORT3 = 9097;
	
    public RestClientTester() {
    	System.out.println("Starting the test RestClientTester");
    }
    /**
     * Junit test to validate the GET request from RestClient
     * Steps:
     * 		1) Create a stub server using Restito framework and configure it to listen on given port
     * 		2) Invoke the sendGETRequest(..) method of RestClient
     * 		3) Restito captures the matching GET requests sent, if any.
     * 		4) Validate if Restito has captured any GET requests on given endpoint
     * Expected Behavior:
     * 		> Restito should have captured GET request and it should have captured only one GET request.
     * Finally:
     * 		> Stop the stub server started using restito.
     */
	public void testGETRequestFromClient() {
		
		StubServer server = null;
		try {
		//This will start the stub server on 'PORT' and responds with HTTP 202 'ACCEPTED_202'
		TestUtil.restartRestitoServerForGETRequests(server, PORT, ACCEPTED_202);
		
		RestClient.sendGETRequest(PORT);
		
		List<Call> callList = TestUtil.waitAndGetCallList(server, 30);
		assertTrue("GET request is not received from the RestClient. Test failed.", 
				(callList != null) && (callList.size() == 1));
		}
		catch(Exception e) {
			e.printStackTrace();
			fail("Test Failed due to exception : " + e);
		}
		finally {
			if(server != null) {
				server.stop();
            }
		}	
	}

========== COD JAVA se terminฤƒ ===========

Pas 4) Cum se valideazฤƒ cererea GET cu anteturi ศ™i cererea POST cu corpul folosind cadrul Restito.

========== COD JAVA รŽncepe ===========

/**
     * Junit test to validate the GET request with headers from RestClient
     * Steps:
     * 		1) Create a stub server using Restito framework and configure it to listen on given port
     * 		2) Invoke the sendGETRequestWithCustomHeaders(..) method of RestClient
     * 		3) Restito captures the matching GET requests sent, if any. 
     * 		4) Validate if Restito has captured any GET requests on a given endpoint
     * Expected Behavior:
     * 		> Restito should have captured GET request, and it should have captured only one GET request.
     * 		> Get the headers of the captured GET request 
     * 		  and make sure the headers match to the ones configured.
     * Finally:
     * 		> Stop the stub server started using restito.
     */

public void testGETRequestWithHeadersFromClient() {
		StubServer server = null;
		
		try {
		//This will start the stub server on 'PORT' and responds with HTTP 202 'ACCEPTED_202'
			TestUtil.restartRestitoServerForGETRequests(server, PORT2, ACCEPTED_202);
		
		RestClient.sendGETRequestWithCustomHeaders(PORT2);
		
		List<Call> callList = TestUtil.waitAndGetCallList(server, 30);
		assertTrue("GET request is not received from the RestClient. Test failed.", 
				(callList != null) && (callList.size() == 1));
		
		//Validate the headers of the GET request from REST Client
		Map<String, List<String>> headersFromRequest = callList.get(0).getHeaders();
		assertTrue("GET request contains header Accept and its value ",
				headersFromRequest.get("Accept").contains("text/html"));
		assertTrue("GET request contains header Authorization and its value ",
				headersFromRequest.get("Authorization").contains("Bearer 1234567890qwertyuiop"));
		assertTrue("GET request contains header Cache-Control and its value ",
				headersFromRequest.get("Cache-Control").contains("no-cache"));
		assertTrue("GET request contains header Connection and its value ",
				headersFromRequest.get("Connection").contains("keep-alive"));
		assertTrue("GET request contains header Content-Type and its value ",
				headersFromRequest.get("Content-Type").contains("application/json"));
		}
		catch(Exception e) {
			e.printStackTrace();
			fail("Test Failed due to exception : " + e);
		}
		finally {
			if(server != null) {
				server.stop();
            }
		}
	}
/**
     * Junit test to validate the POST request with body and headers from RestClient
     * Steps:
     * 		1) Create a stub server using Restito framework and configure it to listen on given port
     * 		2) Invoke the sendPOSTRequestWithJSONBody(..) method of RestClient
     * 		3) Restito captures the matching POST requests sent, if any.
     * 		4) Validate if Restito has captured any POST requests on given endpoint
     * Expected Behavior:
     * 		> Restito should have captured POST request and it should have captured only one POST request.
     * 		> Get the body of the captured POST request and validate the JSON values
     * Finally:
     * 		> Stop the stub server started using restito.
	 */

public void testPOSTRequestWithJSONBody() {
		StubServer server = null;
		
		try {
		//This will start the stub server on 'PORT' and responds with HTTP 202 'ACCEPTED_202'
			TestUtil.restartRestitoServerForPOSTRequests(server, PORT3, ACCEPTED_202);
		
		RestClient.sendPOSTRequestWithJSONBody(PORT3);
		
		List<Call> callList = TestUtil.waitAndGetCallList(server, 30);
		assertTrue("POST request is not received from the RestClient. Test failed.", 
				(callList != null) && (callList.size() == 1));
		
		//Validate the headers of the GET request from REST Client
		
		String requestBody = callList.get(0).getPostBody();
		JSONObject postRequestJSON = new JSONObject(requestBody);
		assertTrue("The timeUpdated in json is incorrect",
				postRequestJSON.get("timeUpdated").toString().equalsIgnoreCase("1535703838478"));
		assertTrue("The access_token in json is incorrect",
				postRequestJSON.get("access_token").toString().
				equalsIgnoreCase("abf8714d-73a3-42ab-9df8-d13fcb92a1d8"));
		assertTrue("The refresh_token in json is incorrect",
				postRequestJSON.get("refresh_token").toString().
				equalsIgnoreCase("d5a5ab08-c200-421d-ad46-2e89c2f566f5"));
		assertTrue("The token_type in json is incorrect",
				postRequestJSON.get("token_type").toString().equalsIgnoreCase("bearer"));
		assertTrue("The expires_in in json is incorrect",
				postRequestJSON.get("expires_in").toString().equalsIgnoreCase("1024"));
		assertTrue("The scope in json is incorrect",
				postRequestJSON.get("scope").toString().equalsIgnoreCase(""));
		}
		catch(Exception e) {
			e.printStackTrace();
			fail("Test Failed due to exception : " + e);
		}
		finally {
			if(server != null) {
				server.stop();
            }
		}
	}
}

========== COD JAVA se terminฤƒ ===========

Avantajele utilizฤƒrii cadrului Restito pentru testarea clientului REST

Iatฤƒ avantajele/beneficiile Cadrului Restito pentru testarea clienศ›ilor ReST

  • Nu avem nevoie ca serverul REST sฤƒ fie dezvoltat pentru a testa clientul REST.
  • Restito oferฤƒ utilitฤƒศ›i ศ™i metode puternice ศ™i variate pentru a bate joc de comportamentul diferit al unui server. De exemplu: pentru a testa modul รฎn care clientul REST se comportฤƒ atunci cรขnd Serverul rฤƒspunde cu eroare HTTP 404 sau eroare HTTP 503.
  • Serverele Restito pot fi configurate รฎn cรขteva milisecunde ศ™i pot fi terminate dupฤƒ finalizarea testelor.
  • Restito acceptฤƒ toate tipurile de conศ›inut al metodei HTTP, cum ar fi comprimat, necomprimat, unificat, aplicaศ›ie/text, aplicaศ›ie/JSON etc.

Dezavantajele utilizฤƒrii cadrului Restito pentru testarea clientului REST

Iatฤƒ dezavantajele/dezavantajele cadrului Restito pentru testarea clienศ›ilor ReST

  • Sursa clientului REST ar trebui sฤƒ fie ajustatฤƒ pentru a considera โ€žlocalhostโ€ ca o maศ™inฤƒ server.
  • Deschiderea serverului รฎn orice port poate intra รฎn conflict dacฤƒ folosim un port folosit รฎn mod obiศ™nuit, cum ar fi โ€ž8080โ€ sau โ€ž9443โ€ etc.
  • Este recomandat sฤƒ utilizaศ›i porturi precum 9092 sau 9099, care nu sunt utilizate รฎn mod obiศ™nuit de alte instrumente.

Rezumat

  • REST รฎnseamnฤƒ โ€žRepresentational State Transferโ€, care este un nou mod standard de comunicare รฎntre oricare douฤƒ sisteme la un moment dat.
  • Clientul REST este o metodฤƒ sau un instrument de invocare a unui serviciu API REST care este expus comunicฤƒrii de cฤƒtre orice sistem sau furnizor de servicii.
  • รŽn metoda RestServer sau un API care este expus pentru comunicare de cฤƒtre orice sistem sau furnizor de servicii.
  • Restito este o aplicaศ›ie uศ™oarฤƒ care vฤƒ ajutฤƒ sฤƒ executaศ›i orice fel de solicitare HTTP
  • Creaศ›i un client HTTP ศ™i o metodฤƒ pentru a trimite o solicitare HTTP GET cฤƒtre orice punct final al serverului
  • Porniศ›i un server Restito pentru a asculta ศ™i captura cererile trimise cฤƒtre punctul final โ€žgeteventsโ€.
  • Porniศ›i un server Restito pentru a asculta ศ™i captura cererile trimise la punctul final โ€žgeteventsโ€ รฎn localhost
  • Aici, am implementat exemple pentru testarea automatฤƒ numai a clientului REST.
  • Nu avem nevoie ca serverul REST sฤƒ fie dezvoltat pentru a testa clientul REST.
  • Sursa clientului REST ar trebui sฤƒ fie ajustatฤƒ pentru a considera โ€žlocalhostโ€ ca o maศ™inฤƒ server.

Rezumaศ›i aceastฤƒ postare cu: