How to extract cookies from a REST Assured response using common code - java

Using REST Assured (4.3.3, from Java) I can freely manipulate request and response specification objects in a base test class, so that when the actual test needs a request specification to execute the actual call, it's "pre-configured" with certain common statements. For example:
public abstract class TestBase
protected RequestSpecification request() {
return given()
.cookies(globalCookies)
.port(serverPort)
.log().ifValidationFails()
.then()
.log().ifValidationFails()
.given();
}
}
public class ActualTest extends TestBase {
#Test
public void test1() {
String content = request().get("/some").then()
.statusCode(200)
.extract()
.body().asString();
}
}
What I'd like to do now is to "pre-configure" the specifications to preserve cookies between the requests. I am feeding the list of cookies to send using RequestSpecification.cookies(Cookies), but I can't find any way to instruct the specifications to extract the returned cookies before obtaining the instance of Response. However, such instance is only available after calling one of the RequestSender methods, which I can't do in the base code.
The methods I've considered to solve this are outlined below, but the first two are quite clunky, and the third is probably just outright wrong as it meddles with classes in "internal" sub-packages. Is there a "right" way of doing this?
Add a base method saveCookies() that takes in an instance of ValidatableResponse, on which it can call extract(), and get the cookies. Then each test will have to use the method.
public abstract class TestBase
// ...
protected ValidatableResponse saveCookies(ValidatableResponse r) {
saveGlobalCookies(r.extract().detailedCookies());
return r;
}
}
public class ActualTest extends TestBase {
#Test
public void test1() {
String content = saveCookies(request().get("/some").then())
.statusCode(200)
.extract()
.body().asString();
}
}
Use a base method that takes in lambdas so that the potentially intermediate ValidatableResponse can be intercepted, and the final result (if needed) returned to the invoker.
public abstract class TestBase
// ...
<T> T cookieRequest(Function<RequestSender, ValidatableResponse> exec, Function<ValidatableResponse, T> post) {
ValidatableResponse vr = exec.apply(request());
saveGlobalCookies(vr.extract().detailedCookies());
return post.apply(vr);
}
}
public class ActualTest extends TestBase {
#Test
public void test1() {
String content = cookieRequest(r->r.get("/some").then(),
r->r.extract().body().asString());
}
}
Instantiate extended instances of ResponseSpecificationImpl (dragging in RequestSpecificationImpl and TestSpecificationImpl).

You can use CookieFilter to satify your need.
The cookie filter can be used to keep track of all the cookies sent by
the server and use them in subsequent requests
You can use this class in 2 ways:
Static setting:
RestAssured.filters(new CookieFilter());
Specify for request you want:
CookieFilter cookieFilter = new CookieFilter();
//Request 1
given().filter(cookieFilter).get("/x");
//Request 2
given().filter(cookieFilter).get("/y");
For more information:
CookieFilter class
CookieFilter test class

Related

Jersey Conditional Pathing

I have a scenario where we support 2 different types of authenticated users (UserTypeA, UserTypeB), but they will never be used in the same server environment. Right now, we use 2 different url paths /path/usertypea/list vs /path/usertypeb/list. We would like to make them use the same path if possible, for example /path/list, and have an environment variable be the condition to know where to route the traffic. The parameters for each user type aren't exactly the same, there are some differences in how the data is organized. We're using Jersey.
I've tried a few things like Singleton classes: https://eclipse-ee4j.github.io/jersey.github.io/documentation/latest/user-guide.html#d0e2650 / https://stackoverflow.com/a/33585724/12183373 but it never routes the value, it just returns the name of the class instead of the JSON payload I'm expecting.
Here's some of the code:
#Path("/list")
public class GlobalSegmentServiceRouter {
#GET
#Produces("application/json")
public Class<?> findAll() {
boolean isUserTypeA = false;
if (isUserTypeA) {
return UserTypeAService.class;
} else {
return UserTypeBService.class;
}
}
}
Then I have 2 separate class files for the following:
#Singleton
public class UserTypeAService {
public List<String> findAll(/*Parameters for A*/) {
// Do work here for User Type A
}
}
#Singleton
public class UserTypeBService {
public List<String> findAll(/*Parameters for B*/) {
// Do work here for User Type B
}
}
When I try and hit the endpoint, this is the response I get:
"com.test.services.UserTypeAService"
Any suggestions on how to accomplish this?
add some flag for checking which kind of user is logged in to a custom principal impl. Then you can inject the current user and then call UserTypeAService.findAll or UserTypeBService.findAll in your method.
#GET
#Path("/path/list")
public String yourMethod(#Context SecurityContext securityContext)

JPA.withTransaction executing other controllers method error: Global.java:39: error: 'void' type not allowed here

I am trying to execute some db insert/update queries in some time intervals.
To achive this I'decided to use Playframework built in Akka Actor system.
I have my class with method:
public class Global extends GlobalSettings {
#Override
public void onStart(Application application) {
Akka.system().scheduler().schedule(
Duration.create(1, TimeUnit.SECONDS), // start task delay
Duration.create(24, TimeUnit.HOURS), // between task instance delay
//Duration.create(24, TimeUnit.HOURS), // between task instance delay
new Runnable() {
#Override
public void run() {
JPA.withTransaction(
ImportCrmData.start()
);
}
},
Akka.system().dispatcher()
);
And the method that is called by the actor system:
public class ImportCrmData extends Controller {
#Transactional
public static void start() {
List<Customer> customersToUpdate = CustomerCRM.importCrmData();
for(Customer c: customersToUpdate) {
c.toDataBase();
}
}
}
I am getting an error on compile:
[error] app/modules/common/app/Global.java:39: error: 'void' type not allowed here ImportCrmData.start()
I understand that the problem occurs cause JPA.withTransaction() demand me to return from ImportCrmData.start() return Callback0 or Function0<>, but I dont know how to do it.
My method is just doing this.persist. Why should I even return something from that?
ImportCrmData is a controller thus it must return a valid http response (a result). A typical use case:
public class CustomerController extends Controller {
public static Result getCustomers() {
List<Customer> customers = CustomerService.getCustomers();
return ok(Json.toJson(customers));
}
}
Above example consists of controller which is an entry point to your application and reacts with client requests. CustomerService encapsulates logic related to getting customers. ok(...) returns an implementation of Result - a valid http response with code 200 and in above scenario, json body. It is implemented in Controller base class. Next your controller can be mapped in routes file to a url like so:
GET /customers controller.CustomerController.getCustomers
Applying above pattern you should have:
CrmController - entry point
CrmService - actual business logic
This separation allows using your CrmService in Global class, as well as in Controller layer without duplicating logic. Mind this is just a suggestion.

Class variable vs passing parameter in java - design issue

Say I have 2 classes in an SOA model application..
Service class - which takes request and returns response
For further processing (say, business logic/parsing/dao etc), it passes the request to a SvcBusiness class.
Question is, should SvcBusiness class use the request as its class variable or should it just use the request in one of it's business methods? It is possible that request needs to be passed to other lower layers like DAO layer. Should those classes also use request as a class variable or should the request be just part of a method?
ServiceImpl class:
public class ServiceImpl {
public Response getDataForType1Request(Request type1) {
SvcBusiness buzclazz = new SvcBusiness();
return buzclazz.doOperationForType1(type1);
}
public Response getDataForType2Request(Request type2) {
SvcBusiness buzclazz = new SvcBusiness();
return buzclazz.doOperationForType2(type2);
}
}
Option 1: when request is passed as a parameter.
public class SvcBusiness {
public Response doOperationForType1(Request type1) {
// do business and return response1
}
public Response doOperationForType2(Request type2) {
// do business and return response2
}
}
Option 2: request is set as a class variable. In this scenario.. ServiceImpl will pass the request to SvcBusiness constructor when the object is created.. and will simply call execute() method.
public class SvcBusiness {
private Request request;
public SvcBusiness(Request request) {
this.request = request;
}
private Response doOperationForType1() {
// do business and return response1
}
private Response doOperationForType2() {
// do business and return response2
}
public Response execute() {
// if type1 request call doOperationForType1()
// if type2 request call doOperationForType1()
}
}
Please help! What are the advantages and disadvantages of both? Is there a design pattern to address this scenario?
Don't use the Request (and Response) further down in your class hierarchy! The service (and everything called by the service) may be called from somewhere else, where there is no such thing as a Request. And then you will have a problem with filling that parameter. Use an own data model in the service, and extract and convert everything you need for that from the Request.
Fully agree with Uwe's answer. However, if you still want to use Request class, it'll be less harmful as a parameter (The way Servlets work). Otherwise, you'd have to deal with synchronization on a highly probable multithreaded environment.
When I face a problem like this I always wonder if I really need an object. Usually I use the option 1 but creating all methods as static. As those methods don't rely in the current object state (there are no instance attributes), I save some memory just not creating such objects (other option is just implement the Singleton pattern).
public class SvcBusiness {
public static Response doOperationForType1(Request type1) {
// do business and return response1
}
public Response doOperationForType2(Request type2) {
// do business and return response2
}
}

GWT RequestFactory returns object of wrong type

When I make a call to fetch a list of TripImportSummaryProxy objects, I get back a list of:
com.schedgy.core.dao.filter.proxy.FilterProxyAutoBean_com_google_web_bindery_requestfactory_shared_impl_EntityProxyCategory_com_google_web_bindery_requestfactory_shared_impl_ValueProxyCategory_com_google_web_bindery_requestfactory_shared_impl_BaseProxyCategory.
#ProxyFor(value=TripImportSummary.class, locator=TripImportSummaryLocator.class)
public interface TripImportSummaryProxy extends MyBaseProxy {
// some setter/getters defined here
}
public interface TripImportSummaryRequestFactory extends RequestFactory, HasPaginationRequest<TripImportSummaryProxy> {
TripImportSummaryRequest request();
}
#Service(value=TripImportSummaryService.class, locator=MyServiceLocator.class)
public interface TripImportSummaryRequest extends RequestContext, PaginationRequest<TripImportSummaryProxy> {
}
#SkipInterfaceValidation
public interface HasPaginationRequest<T> extends RequestFactory {
PaginationRequest<T> request();
}
#ExtraTypes(FilterProxy.class)
#SkipInterfaceValidation
public interface PaginationRequest<T> extends RequestContext {
Request<List<T>> paginate(int offset, int limit, String sortColumn,
boolean isSortAscending, List<FilterProxy> filters);
Request<Integer> count(List<FilterProxy> list);
}
This is all executed via:
PaginationRequest<TripImportSummaryProxy> request = requestFactory.request();
request.paginate(offset, limit, sortColumn, isSortAscending, getFilters(request)).with(getPaths()).fire(new MyReceiver<List<TripImportSummaryProxy>>() {
#Override
public void onSuccess(List<TripImportSummaryProxy> response) {
// Response is a list of type that seems to extend from FilterProxy
}
});
FilterProxy is just a marker interface that various filter interfaces extend.
#ProxyFor(Object.class)
public interface FilterProxy extends ValueProxy {
}
I have about two dozen other requests working and its only failing on this one. I have verified that the server side service is correctly fetching and returning the right data. I have found that the TripImportSummaryLocator class is never instantiated even though it appears to be bound to the proxy type correctly and has a default constructor.
I was using GWT 2.4 rc1 and after upgrading to GWT 2.4 stable I am no longer seeing this problem.

Does Restlet support parsing URLs into calling methods with parameters?

Take the following example.
I have a resource
public class HelloWorldResource extends ServerResource {
#Get
public String represent(String arg) {
return "hello, world (from the cloud!)" + arg;
}
}
That is mapped by
router.attach("/hi/{message}", HelloWorldResource.class);
Is it possible to configure the routing such that accessing /hi/somestuffhere will make restlet fill in the arg parameter in the represent method?
Try this:
String msg = (String) getRequest().getAttributes().get("message");
You can place this in an overriden doInit() method in order to let this happen automatically for all your requests to this resource.

Categories