Accessing REST End-point using Feign Client
Introduction
This blog is based on the Spring Boot Stock App, one of my previous blog for details on the blog.
TDD (Test Driven Development) is important for any enterprise level application development.
In this blog we will show sample code example to using Feign API client to test the application.
- First we define the end points in the Interface.
- We use the interface methods, to invoke the end-point.
Include the necessary dependency in pom.xml
<!-- Feign based API access jar dependency PLAIN -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-core</artifactId>
<version>11.7</version>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
<version>11.7</version>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-jackson</artifactId>
<version>11.7</version>
</dependency>
Interface to implement the end-point
package com.stock.finance.client;
import java.util.List;
import com.stock.finance.model.AuthenticationRequest;
import com.stock.finance.model.StockInfoWrapper;
import com.stock.finance.model.TokenRequest;
import com.stock.finance.model.api.About;
import com.stock.finance.model.api.AuthenticationResponse;
import com.stock.finance.model.api.SimpleStatusResponse;
import feign.Headers;
import feign.Param;
import feign.RequestLine;
/*
* Feign client based on the interface will create the http client.
*/
public interface StockAppFeignClient {
// below is feign annotation which will allow to describe the http request
// used to make the HTTP call
@RequestLine("GET /stock-app/info") // NOTE: No domain name is provided here.
//@Headers({"Content-Type: application/json"}) // this is static, but there is template support to pass dynamic values to header
// for example to pass this in the jwt token
// Create a method that will create the request.
// The return type will be used on which the response will de-serialized.
public Object getAppInfo();
@RequestLine("GET /stock-app/about")
public About getAboutApp();
@RequestLine("POST /stock-app/signup")
@Headers({"Content-Type: application/json"})
public SimpleStatusResponse signUp(AuthenticationRequest request);
@RequestLine("POST /stock-app/apikey")
@Headers({"Content-Type: application/json"})
public SimpleStatusResponse getAPIKey(AuthenticationRequest request);
@RequestLine("POST /stock-app/token")
@Headers({"Content-Type: application/json"})
public AuthenticationResponse getToken(TokenRequest request);
@RequestLine("GET /stock/v1/get")
@Headers({"Content-Type: application/json","Authorization: Bearer {token}"})
public List<StockInfoWrapper> getStock(@Param("token") String token);
}
Implementing the Interface for testing end point
- The actual test case using the Interface, we are not adding any new stock, for example we are just creating a new user.
- We are using Junit 5 dependencies.
package com.stock.finance.client;
import java.util.List;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import com.stock.finance.model.AuthenticationRequest;
import com.stock.finance.model.StockInfoWrapper;
import com.stock.finance.model.TokenRequest;
import com.stock.finance.model.api.About;
import com.stock.finance.model.api.AuthenticationResponse;
import com.stock.finance.model.api.SimpleStatusResponse;
import feign.Feign;
import feign.jackson.JacksonDecoder;
import feign.jackson.JacksonEncoder;
// the feign interface is already defined in the test package
// this test class will be using the interface to invoke the stock app
//@Disabled
@SpringBootTest
public class TestStockAPIFeignClient {
private static String ENDPOINT_DOMAIN = "https://my-stock-boot-app.herokuapp.com/";
// NOTE: Add the feign-okhttp dependency
StockAppFeignClient client = null;
@BeforeEach
public void setup() {
client = Feign.builder()
.decoder(new JacksonDecoder()) // this will be used by feign client to de-serialize the response body
.encoder(new JacksonEncoder()) //this will be responsible for serializing the request
.target(StockAppFeignClient.class,ENDPOINT_DOMAIN); // pass the interface client as class ref
}
@Test
void getAppInfo() {
Object info = client.getAppInfo();
System.out.println(info.toString());
Assertions.assertNotNull(info);
Assertions.assertTrue(info.toString().contains("Stock"),"The returned response should have Stock in it");
}
@Test
void getAppVersion() {
About about = client.getAboutApp();
Assertions.assertNotNull(about);
Assertions.assertTrue(about.getVersion().contentEquals("v1"),"The returned response should have v1 in it");
}
@Test
// used to disable the test case from executing
//@Disabled
// This test case only for the very first user.
void testSignUpAndGetStock() {
// 1. signup with test account
// 2. get the API key
// 3. generate the token - with API key
// 4. hit the get passing token in Authorization header
//1.
final String user = "testuser";
final String password = "password";
AuthenticationRequest request = new AuthenticationRequest(user, password);
SimpleStatusResponse apiKeyRespsone = client.signUp(request);
Assertions.assertNotNull(apiKeyRespsone);
// if this value is null, then the user info already exists in db
Assertions.assertNotNull(apiKeyRespsone.getApiKey());
Assertions.assertTrue(apiKeyRespsone.getStatusMessage().contains(user));
//2. Get the api key
String apiKey = apiKeyRespsone.getApiKey();
//3
TokenRequest tokenRequest = new TokenRequest(user,apiKey);
AuthenticationResponse tokenResponse = client.getToken(tokenRequest);
Assertions.assertNotNull(tokenResponse.getApiKey());
Assertions.assertNotNull(tokenResponse.getJwtToken());
String tokenInfo = tokenResponse.getJwtToken();
//4
List<StockInfoWrapper> stock = client.getStock(tokenInfo); // expected to be empty
Assertions.assertNotNull(stock);
}
@Test
void testgetApiKeyAndGetStock() {
// 1. get the API key
// 2. generate the token - with API key
// 3. hit the get passing token in Authorization header
//1.
final String user = "testuser";
final String password = "password";
AuthenticationRequest request = new AuthenticationRequest(user, password);
SimpleStatusResponse apiKeyRespsone = client.getAPIKey(request); //Endpoint
Assertions.assertNotNull(apiKeyRespsone);
// if this value is null, then the user info already exists in db
Assertions.assertNotNull(apiKeyRespsone.getApiKey());
String apiKey = apiKeyRespsone.getApiKey();
//2
TokenRequest tokenRequest = new TokenRequest(user,apiKey);
AuthenticationResponse tokenResponse = client.getToken(tokenRequest);
//Assertions.assertNotNull(tokenResponse.getApiKey());
Assertions.assertNotNull(tokenResponse.getJwtToken());
String tokenInfo = tokenResponse.getJwtToken();
//3
List<StockInfoWrapper> stock = client.getStock(tokenInfo); // expected to be empty
Assertions.assertNotNull(stock);
Assertions.assertTrue(stock.isEmpty());
}
}