I'm learning to create a REST Assured and Cucumber framework from scratch following a tutorial video on Youtube.
Below is the step definition and the method it calls in the RestAssuredExtension class.
#Given("^I perform GET operation for \"([^\"]*)\"$")
public void i_Perform_GET_Operation_For(String url) throws Throwable {
RestAssuredExtension.GetOps(url);
}
package utilities;
import io.restassured.RestAssured;
import io.restassured.builder.RequestSpecBuilder;
import io.restassured.http.ContentType;
import io.restassured.response.Response;
import io.restassured.response.ResponseOptions;
import io.restassured.specification.RequestSpecification;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Map;
public class RestAssuredExtension {
public static RequestSpecification Request;
public RestAssuredExtension() {
//Arrange
RequestSpecBuilder builder = new RequestSpecBuilder();
builder.setBaseUri("http://localhost:3000/");
builder.setContentType(ContentType.JSON);
var requestSpec = builder.build();
Request = RestAssured.given().spec(requestSpec);
}
public static ResponseOptions<Response> GetOps(String url) {
//Act
try {
return Request.get(new URI(url));
} catch (URISyntaxException e) {
e.printStackTrace();
}
return null;
}
}
In the video tutorial, the test passes successfully. But when I run the test myself, it results in the following error:
Step failed
java.lang.NullPointerException: Cannot invoke "io.restassured.specification.RequestSpecification.get(java.net.URI)" because "utilities.RestAssuredExtension.Request" is null
at utilities.RestAssuredExtension.GetOps(RestAssuredExtension.java:42)
at steps.GETPostSteps.i_Perform_GET_Operation_For(GETPostSteps.java:21)
Any takers please?
From the example you have given, I think you have not initialized the RestAssuredExtension.Request field.
In the video (I quickly skimmed it), they provide a hook to create a new instance of the RestAssuredExtension before any tests are executed. This will ensure that the public static class variable Request will have been initialized to a non-null value.
My recommendation, if you want to reduce dependency for setup on the test framework and make use of static methods:
public final class RequestExtension {
private static RequestSpecification request;
// Ensure that no one is able to create an instance and thereby bypass proper initalization
private RequestExtension() {
}
// Ensures the initialization responsibility is within the class itself and not a hidden dependency for other users.
private static void getInstance() {
if (request == null) {
RequestSpecBuilder builder = new RequestSpecBuilder();
builder.setBaseUri("http://localhost:3000/");
builder.setContentType(ContentType.JSON);
var requestSpec = builder.build();
request = RestAssured.given().spec(requestSpec);
}
return request;
}
public static ResponseOptions<Response> GetOps(String url) {
// Initialize
getInstance();
// Act
try {
return request.get(new URI(url));
} catch (URISyntaxException e) {
e.printStackTrace();
}
return null;
}
}
Otherwise, mixing static methods with dependencies on the instance will keep tripping people up. Would go either with the above or remove static from the class altogether:
public class RequestExtension {
private RequestSpecification request;
public RestAssuredExtension() {
//Arrange
RequestSpecBuilder builder = new RequestSpecBuilder();
builder.setBaseUri("http://localhost:3000/");
builder.setContentType(ContentType.JSON);
var requestSpec = builder.build();
request = RestAssured.given().spec(requestSpec);
}
public ResponseOptions<Response> GetOps(String url) {
//Act
try {
return request.get(new URI(url));
} catch (URISyntaxException e) {
e.printStackTrace();
}
return null;
}
}
One thing to help with debugging is to follow Java naming conventions. The capitalisation of your class field RequestSpecification makes it read as a class not a field name. (Request vs request) It was the same in the video so its a source issue. :)
Related
I maintain a small protobuf based REST-ful API using Jersey. I am working on a small project that can call protobuf api via json proxy endpoint. To illustrate my idea, here is the list of resources and endpoints:
PersonResource
GET /getPerson -> #returns instance of PersonProto
POST /person -> create person and #returns instance of PersonProto
SubscriptionResource
GET /getSubscriptions(personId) -> #returns List<SubscriptionProto>
etc
Now using a postman like client, user can hit a protobuf request via a json proxy endpoint by passing a json request instead of protobuf message. So i d like my proxy endpoint to handle back and forth conversion between json and protobuf. Is there a reliable way to do it using Java 11 and Protocol-buffers version 3? If so could you share please some examples?
You can use reflection to access protobuf objects and create mapping between protobuf and json.
I knew we could use reflection from the beginning but I thought there may be a simpler way exist to accomplish the same. Here is the helper class that I produced for such use-case:
If you know the path to a message class or have reference to a message class somehow, then you can use JsonFormat utility of protobuf.
package com.apple.amp.commerce.support.protodive.util;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.Message;
import com.google.protobuf.util.JsonFormat;
import java.io.UncheckedIOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ProtoBufJsonSerializer {
private static final JsonFormat.Parser JSON_FORMAT_PARSER = JsonFormat.parser().ignoringUnknownFields();
private static final JsonFormat.Printer JSON_FORMAT_PRINTER = JsonFormat.printer().includingDefaultValueFields();
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
public JsonNode getJsonNodeFromMessage(Message message) {
final String jsonString = getJsonStringMessage(message);
try {
return OBJECT_MAPPER.readTree(jsonString);
} catch (JsonProcessingException e) {
throw new UncheckedIOException(e);
}
}
private String getJsonStringMessage(Message message) {
try {
return JSON_FORMAT_PRINTER.print(message);
} catch (InvalidProtocolBufferException e) {
throw new IllegalStateException("Unable to transform message to json", e);
}
}
public Message getMessageFromJson(String messageClassName, JsonNode requestPayload) {
Message.Builder msgBuilder = getMessageBuilder(messageClassName);
try {
String jsonString = requestPayload.toString();
JSON_FORMAT_PARSER.merge(jsonString, msgBuilder);
return msgBuilder.build();
} catch (InvalidProtocolBufferException e) {
throw new IllegalStateException(e);
}
}
private Message.Builder getMessageBuilder(String messageClassName) {
try{
Class<? extends Message> msgClass = (Class<? extends Message>) Class.forName(messageClassName);
Method toBuilderMethod = msgClass.getMethod("getDefaultInstance");
Message msg = (Message) toBuilderMethod.invoke(null);
return msg.toBuilder();
} catch(IllegalAccessException | ClassNotFoundException | NoSuchMethodException | InvocationTargetException e){
throw new IllegalStateException("Unable to transform json into message", e);
}
}
}
I would like to take a screenshot every single time test fails or if multiple tests then multiple screenshots of course.
So far I understand that I can just wrap my single test with a try catch block and proceed taking a screenshot, however I would not want to wrap it in every test I have. I want it to apply to all of them without wrapping each one, do I have to do that in my setup?
public class WebDriverSettings
{
protected WebDriver driver;
protected String TARGET_URL;
#BeforeEach
public void setUp()
{
WebDriverManager.chromedriver().setup();
driver = new ChromeDriver(new ChromeOptions().addArguments("window-size=1920x1480"));
driver.manage().timeouts().implicitlyWait(15, TimeUnit.SECONDS);
loginToEnvironment();
}
}
public class LoginServiceTest extends WebDriverSettings
{
private LoginModal loginModal;
private AccountApi accountApi;
private Credentials credentials;
#BeforeEach
public void setUp()
{
super.setUp();
credentials = new SignUp();
accountApi = new AccountApi(credentials);
accountApi.createAccount();
loginModal = new HomePage(driver).acceptCookies().clickOnMyAccountTab().switchToLoginTab();
}
#Test
public void shouldSuccessfullyLogin()
{
try
{
accountApi.createAccount();
assertFalse(loginModal.login(credentials).getMyAccountName().getText().isEmpty());
} catch (Exception e)
{
try
{
File screenshotFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
FileUtils.copyFile(screenshotFile, new File("path"));
} catch (IOException ioException)
{
ioException.printStackTrace();
}
accountApi.closeAccount();
}
}
}
Solution advised by Jeff
So creating Util package and adding a class that would be responsible for creating a screenshot also it would generate random name but needs to be refactored i just made it quickly to make it work
public class ScreenShotCreator {
public static void takeScreenShot(WebDriver driver) {
try {
File screenshotFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
FileUtils.copyFile(screenshotFile, new File(fileNameGenerator()));
} catch (IOException e) {
throw new RuntimeException("Could not make a screenshot");
}
}
// creating this for test purposes , need to use string builder instead to append it instead of adding it
private static String fileNameGenerator() {
SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy-HH:mm");
String path = ".....";
return path + "screenshot" + formatter.format(new Date()) + " " + RandomStringUtils.randomAlphanumeric(10) + ".png";
}
Then before closing it down just call the created method
#AfterEach
public void tearDown() {
ScreenShotCreator.takeScreenShot(driver);
driver.manage().deleteAllCookies();
driver.close();
driver.quit();
}
The solution that worked for me was to create this extension:
package some.thing;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.jupiter.api.extension.AfterTestExecutionCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
public class Screenshotter implements AfterTestExecutionCallback {
private static final Logger LOG = LogManager.getLogger();
private static WebDriver driver;
public static void setDriver(WebDriver driver) {
Screenshotter.driver = driver;
}
#Override
public void afterTestExecution(ExtensionContext context) throws Exception {
if (context.getExecutionException().isPresent()) { // if the test execution has failed
String baseFileName = context.getRequiredTestClass().getSimpleName() + "-"
+ context.getRequiredTestMethod().getName()
+ LocalDateTime.now().format(DateTimeFormatter.ofPattern("-yyMMdd-HHmmss"));
File targetFile = new File("somewhere/" + baseFileName + ".png");
File scrFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
Files.copy(scrFile.toPath(), targetFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
targetFile.setReadable(true, false);
LOG.info("Screenshot saved to " + targetFile.toPath());
}
}
}
Then apply it to the relevant test class like so:
#ExtendWith(Screenshotter.class)
public class SomeTest {
...
#BeforeAll
public void initialize() {
...
Screenshotter.setDriver(driver);
...
}
...
}
What I would suggest is
Create a method to take a screenshot and put that in a Utils class and call it when you want to take a screenshot. This will make taking a screenshot a lot easier because all the code lives in one place and can be easily called from anywhere.
Create a tearDown() method, if you don't have one already. Looks like it would go in the WebDriverSettings class from your currently posted code. Mark it with an #AfterEach annotation and then detect a failed test case and if it failed, take a screenshot.
If you aren't sure how to do that, there's a class in JUnit 4.9 and later called TestWatcher that you can use. There are lots of examples on the web on how to use it.
Intellij keeps saying Undefined Step when running my feature file. However, I have copied the steps and put them into another package and added that package name in the "glue" parameter for Edit configurations. Still not working.
I've added the feature file and it doesn't show any missing step references. I am adding a screenshot. I have added all the steps in another package.
Please see below
The code for CountriesSteps is as follows
package Steps;
import Fixtures.Countries;
import cucumber.api.java.en.Given;
import cucumber.api.java.en.Then;
import cucumber.api.java.en.When;
import org.junit.Assert;
public class CountriesSteps {
Countries countriesclass = new Countries();
#Given("^I generate a restful request for countries$")
public void iGenerateARestfulRequestForCountries() throws Throwable {
countriesclass.GetCountries();
}
#When("^I receive a successful country response (.*)$")
public void iReceiveASuccessfulCountryResponseResponse(int code) throws Throwable {
Assert.assertEquals(code, countriesclass.getsCode());
}
#When("^I receive a successful country response$")
public void iReceiveASuccessfulCountryResponse() throws Throwable {
Assert.assertEquals(200, countriesclass.getsCode());
}
#Then("^the api country response returns (.*)$")
public void theApiCountryResponseReturnsCountries(int countries) throws Throwable {
Assert.assertEquals(countries, countriesclass.getCount());
}
#Then("^the api country response returns (.*),(.*),(.*),(.*),(.*)$")
public void theApiCountryResponseReturnsCountriesNameCapitalPopulation(int countries, int index, String name, String capital, int population) throws Throwable {
Assert.assertEquals(countries, countriesclass.getCount());
}
#Then("^the api country response for Index (.*) returns (.*),(.*),(.*)$")
public void theApiCountryResponseForIndexIndexReturnsNameCapitalPopulation(int index, String name, String capital, int population) throws Throwable {
//Validate a few values from response
Assert.assertEquals(name, countriesclass.getcList().get(index).name);
Assert.assertEquals(capital, countriesclass.getcList().get(index).capital);
Assert.assertEquals(population, countriesclass.getcList().get(index).population);
}
}
And code for Countries is
package Fixtures;
import Models.CountriesData;
import com.jayway.restassured.response.Response;
import gherkin.deps.com.google.gson.Gson;
import gherkin.deps.com.google.gson.reflect.TypeToken;
import org.json.JSONArray;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import static com.jayway.restassured.RestAssured.get;
public class Countries {
private static String url;
private static int count;
private static int sCode;
private static List<CountriesData> cList;
public void GetCountries() throws Exception
{
try {
url = "http://restcountries.eu/rest/v1/all";
// make get request to fetch json response from restcountries
Response resp = get(url);
//Fetching response in JSON as a string then convert to JSON Array
JSONArray jsonResponse = new JSONArray(resp.asString());
count = jsonResponse.length(); // how many items in the array
sCode = resp.statusCode(); // status code of 200
//create new arraylist to match CountriesData
List<CountriesData> cDataList = new ArrayList<CountriesData>();
Gson gson = new Gson();
Type listType = new TypeToken<List<CountriesData>>() {}.getType();
cDataList = gson.fromJson(jsonResponse.toString(), listType);
cList = cDataList;
}
catch (Exception e)
{
System.out.println("There is an error connecting to the API: " + e);
e.getStackTrace();
}
}
//getters to return ('get) the values
public int getsCode() {
return sCode;
}
public int getCount() {
return count;
}
public List<CountriesData> getcList() {
return cList;
}
}
When creating a new step definition file, IntelliJ by default proposes file path of \IdeaProjects\RestAPI\src\main\resources\stepmethods.
Your step definition folder is \IdeaProjects\RestAPI\src\test\java\Steps. Make sure cucumber isn't looking in the wrong place.
I want to connect Java with JIRA trial account. I tested this code:
public class JiraImpl
{
private static URI JIRA_URL = URI.create("https://sonoratest.atlassian.net");
private static final String JIRA_ADMIN_USERNAME = "sonoratestw#gmail.com";
private static final String JIRA_ADMIN_PASSWORD = "sonpass";
public static void main(String[] args) throws IOException, InterruptedException, ExecutionException
{
try
{
AsynchronousJiraRestClientFactory factory = new AsynchronousJiraRestClientFactory();
JiraRestClient restClient = factory.createWithBasicHttpAuthentication(JIRA_URL, JIRA_ADMIN_USERNAME, JIRA_ADMIN_PASSWORD);
Iterable<BasicProject> allProjects = restClient.getProjectClient().getAllProjects().claim();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
But when I run it nothing happens. Wahat is the proper way to get data from JIRA using REST API?
Update. I also tried this:
private static URI JIRA_URL = URI.create("https://sonoratest.atlassian.net/rest/auth/1/session");
I get
java.util.concurrent.ExecutionException: RestClientException{statusCode=Optional.of(404), errorCollections=[ErrorCollection{status=404, errors={}, errorMessages=[]}]}
at com.google.common.util.concurrent.AbstractFuture$Sync.getValue(AbstractFuture.java:299)
at com.google.common.util.concurrent.AbstractFuture$Sync.get(AbstractFuture.java:286)
at com.google.common.util.concurrent.AbstractFuture.get(AbstractFuture.java:116)
at com.google.common.util.concurrent.ForwardingFuture.get(ForwardingFuture.java:63)
at com.atlassian.jira.rest.client.internal.async.DelegatingPromise.get(DelegatingPromise.java:102)
at com.jira.impl.JiraImpl.main(JiraImpl.java:23)
Caused by: RestClientException{statusCode=Optional.of(404), errorCollections=[ErrorCollection{status=404, errors={}, errorMessages=[]}]}
Try getting an issue first, since that is so basic.
import java.net.URI;
import java.util.Optional;
import com.atlassian.jira.rest.client.api.JiraRestClient;
import com.atlassian.jira.rest.client.api.domain.Issue;
import com.atlassian.jira.rest.client.internal.async.AsynchronousJiraRestClientFactory;
import com.atlassian.util.concurrent.Promise;
public class JRC
{
public Issue getIssue(String issueKey) throws Exception
{
final URI jiraServerUri = new URI("https://jira-domain");
final JiraRestClient restClient = new AsynchronousJiraRestClientFactory().createWithBasicHttpAuthentication(jiraServerUri, "user#domain.com", "password");
Promise issuePromise = restClient.getIssueClient().getIssue(issueKey);
return Optional.ofNullable((Issue) issuePromise.claim()).orElseThrow(() -> new Exception("No such issue"));
}
}
You can also take a look at this code to get a fully working sample:
https://github.com/somaiah/jrjc
Lately i have been trying to make communication between minecraft server (running with Java) and scratch (running with JavaScript).
I have written the code in java already:
package me.yotam180;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Map;
import org.bukkit.Bukkit;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
public class HttpProcessor {
public MainClass plugin;
public HttpProcessor (MainClass plug) throws IOException {
plugin = plug;
plugin.getLogger().info("CREATED HTTTP PROCESSOR");
HttpServer server = HttpServer.create(new InetSocketAddress(9090), 0);
server.createContext("/pollplayer", new PollPlayerHandler());
server.createContext("/killplayer", new KillPlayerHandler());
plugin.getLogger().info("STARTED HTTTP SERVER");
server.setExecutor(null); // creates a default executor
server.start();
}
static class PollPlayerHandler implements HttpHandler {
#SuppressWarnings("deprecation")
#Override
public void handle(HttpExchange httpExchange) throws IOException {
// TODO Auto-generated method stub
Map <String,String>parms = HttpProcessor.queryToMap(httpExchange.getRequestURI().getQuery());
StringBuilder response = new StringBuilder();
response.append(Bukkit.getPlayer(parms.get("name")).getLocation().toString());
HttpProcessor.writeResponse(httpExchange, response.toString());
}
}
static class KillPlayerHandler implements HttpHandler {
#SuppressWarnings("deprecation")
#Override
public void handle(HttpExchange httpExchange) throws IOException {
// TODO Auto-generated method stub
Map <String,String>parms = HttpProcessor.queryToMap(httpExchange.getRequestURI().getQuery());
Bukkit.getPlayer(parms.get("name")).setHealth(0);
HttpProcessor.writeResponse(httpExchange, "SUCCESS");
}
}
public static void writeResponse(HttpExchange httpExchange, String response) throws IOException {
httpExchange.sendResponseHeaders(200, response.length());
OutputStream os = httpExchange.getResponseBody();
os.write(response.getBytes());
os.close();
}
public static Map<String, String> queryToMap(String query){
Map<String, String> result = new HashMap<String, String>();
for (String param : query.split("&")) {
String pair[] = param.split("=");
if (pair.length>1) {
result.put(pair[0], pair[1]);
}else{
result.put(pair[0], "");
}
}
return result;
}
}
Now I have to make the scratch side HTTP Client. Every way i tried, It just didn't work. I try to open my browser, i write http://localhost:9090/pollplayer?name=yotam_salmon and it reports my player location beautifully. Now my problmem is the scratch JS.
Here it is:
new (function () {
var ext = this;
// Cleanup function when the extension is unloaded
ext._shutdown = function () { };
// Status reporting code
// Use this to report missing hardware, plugin or unsupported browser
ext._getStatus = function () {
return { status: 2, msg: 'Ready' };
};
ext.get_Player = function (name, callback) {
//in this function i need to call http://localhost:9090/pollplayer?name= + name, wait for the response and then callback it.
//the response can't be "return response;", and it cannot be call backed from another function. If this function was called, it
//has to report the location back as a string
};
// Block and block menu descriptions
var descriptor = {
blocks: [
['R', 'location of %s', 'get_Player', 'Player'],
]
};
// Register the extension
ScratchExtensions.register('ScratchCraft', descriptor, ext);
})();
I cannot format my JS code differently, because Scratch works only with this format.(It is explained here: http://llk.github.io/scratch-extension-docs/). In the ext.get_Player function i have to go to the Java http server, request /pollplayer?name= + name, and callback it .
I would be happy to get a solution :) Thanks!
The solution was very simple. I just had to add an header of "Allow-Access-Cross-Origin", and it was solved.
httpExchange.getResponseHeaders().set("Access-Control-Allow-Origin", "*");
httpExchange.getResponseHeaders().set("Content-Type", "text/plain");