Simple library to manipulate HTTP requests and responses, capture the network logs made by the browser using selenium tests without using any proxies
Although selenium is used for e2e and especially UI testing, you might want to mock certain HTTP response to test few error scenarios and to assess HTTP requests done by your client code (e.g. when you don't have immediate UI feedback, like in metrics or tracking calls).
There's one catch though: you can't intercept HTTP calls that are initiated on page load (like in most SPAs), as it requires some setup work that can only be done after the page is loaded (due to limitations in selenium). That means you can just capture requests that were initiated inside a test. If you're fine with that, this plugin might be for you, so read on.
<dependency>
<groupId>io.github.sudharsan-selvaraj</groupId>
<artifactId>wow-xhr</artifactId>
<version>1.0.0</version>
</dependency>
implementation group: 'io.github.sudharsan-selvaraj', name: 'wow-xhr', version: '1.0.0'
Also while downloading selenium, make sure to exclude net.bytebuddy:byte-buddy
library by using
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>3.141.59</version>
<exclusions>
<exclusion>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
</exclusion>
</exclusions>
</dependency>
implementation (group: 'org.seleniumhq.selenium', name: 'selenium-java', version: '3.141.59') {
exclude group: 'net.bytebuddy', module: 'byte-buddy'
}
- Intercept any HTTP request and modify the header, query param, body of the request.
- Modify the header, body, status of the HTTP response.
- Get the complete details of all API calls made by the browser along with the header, body, initiated time and completed time.
- Add a delay to any API call to simulate network speed for specific API call.
Create wowXhr object,
WowXHR wowXhr = new WowXHR(new ChromeDriver()); //any selenium webdriver or RemoteWebDriver
WebDriver driver = wowXhr.getMockDriver()
That's it. Now the driver object can be used in the test. No extra configurations required for browser running on remote machine or browserstack or saucelabs.
import static io.github.sudharsan_selvaraj.wowxhr.mock.Mockable.*;
driver.get("http://localhost:3000");
wowXHR.mock().add(
whenGET("/api")
.modifyRequest(
mockRequest()
.setHeader("Authorization", "invalid-auth-token")
)
);
Above mock will update the Authorization
header with invalid-auth-token
for all HTTP request that has /api
regular expression in its url.
import static io.github.sudharsan_selvaraj.wowxhr.mock.Mockable.*;
driver.get("http://localhost:3000");
wowXHR.mock().add(
whenGET("/api/users")
.modifyRequest(
mockRequest()
.setQueryParam("filter_fname", "sudharsan")
)
);
Above mock will add ?filter_fname=sudharsan
or update the value of filter_fname
query param for all /api/users
api requests.
import static io.github.sudharsan_selvaraj.wowxhr.mock.Mockable.*;
driver.get("http://localhost:3000");
wowXHR.mock().add(
whenPOST("/api/login")
.modifyRequest(
mockRequest()
.setBody("{\"email\":\"invalidemail@emial.com\",\"password\":\"somepassword\"}")
)
);
Above mock will update the request body with {\"email\":\"invalidemail@emial.com\",\"password\":\"somepassword\"}
for all subsequent /api/login
requests.
import static io.github.sudharsan_selvaraj.wowxhr.mock.Mockable.*;
driver.get("http://localhost:3000");
wowXHR.mock().add(
whenPOST("/api/login")
.respond(
mockResponse()
.withHeader("Set-Cookie", "refresh-token=invalid-refresh-token; expires=Mon, 17-Jul-2021 16:06:00 GMT; Max-Age=31449600; Path=/; secure")
)
);
Above mock will update the response header and add's the new cookie to the browser.
import static io.github.sudharsan_selvaraj.wowxhr.mock.Mockable.*;
driver.get("http://localhost:3000");
wowXHR.mock().add(
whenPOST("/api/login")
.respond(
mockResponse()
.withBody("{\"error\": \"true\" ,\"message\" : \"User account is locked\"}")
)
);
Above mock will update the response body with "{\"error\": \"true\" ,\"message\" : \"User account is locked\"}"
for all login api calls.
import static io.github.sudharsan_selvaraj.wowxhr.mock.Mockable.*;
driver.get("http://localhost:3000");
wowXHR.mock().add(
whenPOST("/api/login")
.respond(
mockResponse()
.withStatus(500) //internal server error
)
);
Above mock will change the response status code to 500
and makes the application to assume that the API call has been failed.
import static io.github.sudharsan_selvaraj.wowxhr.mock.Mockable.*;
driver.get("http://localhost:3000");
wowXHR.mock().add(
whenPOST("/api/login")
.respond(
mockResponse()
.withDelay(10) //in seconds
)
);
Above mock will add a delay of 10 seconds before the response is returned to the application. This will be very useful in testing any loading UI elements that is displayed when API calls are made.
At any point in the test if you decide to pause the mocking, then it can be achieved using
wowhXhr.mock().pause();
and you can resume using,
wowhXhr.mock().resume();
List<XHRLog> logs = wowXHR.log().getXHRLogs();
logs.forEach(log -> {
Date initiatedTime = log.getInitiatedTime();
Date completedTime = log.getCompletedTime();
String method = log.getRequest().getMethod().name(); // GET or POST or PUT etc
String url = log.getRequest().getUrl();
Integer status = log.getResponse().getStatus();
String requestBody = (String) log.getRequest().getBody();
String responseBody = (String) log.getResponse().getBody();
Map<String, String> requestHeaders = log.getRequest().getHeaders();
Map<String, String> responseHeaders = log.getResponse().getHeaders();
});
Note: By default wowXHR.log().getXHRLogs()
will clear the previously fetched logs. If you want the logs to be preserved, then you can use the overloaded method
wowXHR.log().getXHRLogs(false)
wowXHR.log().clear();
- Support for making the tests to wait for all api calls to get completed (similar to
protractor's
waitForAngular
) - Ability to mock the HTTP requests that are triggered during initial page load.
- Currently, the request can be mocked based on its URL only(Regex). In the future, we will also support for finding requests using query params(partial and exact) and body(partial and exact).
- TBD