Integration tests for Rest API - spring

I'd like to get different pointe of views about how to create integration tests for Rest APIs.
The first option would be using cucumber as described in the "The Cucumber Book":
Scenario: Get person
Given The system knows about the following person:
| fname | lname | address | zipcode |
| Luca | Brow | 1, Test | 098716 |
When the client requests GET /person/(\d+)
Then the response should be JSON:
"""
{
"fname": "Luca",
"lname": "Brow",
"address": {
"first": "1, Test",
"zipcode": "098716"
}
}
"""
The second option would be (again) using cucumber, but removing the technical detail as described here:
Scenario: Get person
Given The system knows about the following person:
| fname | lname | address | zipcode |
| Luca | Brow | 1, Test | 098716 |
When the client requests the person
Then the response contains the following attributes:
| fname | Luca |
| lname | Brow |
| address :first | 1, Test |
| address :zipcode | 098716 |
And the third option would be using Spring as described here:
private MockMvc mockMvc;
#Test
public void findAll() throws Exception {
mockMvc.perform(get("/person/1"))
.andExpect(status().isOk())
.andExpect(content().mimeType(IntegrationTestUtil.APPLICATION_JSON_UTF8))
.andExpect(jsonPath("$.fname", is("Luca")))
.andExpect(jsonPath("$.lname", is("Brow")))
.andExpect(jsonPath("$.address.first", is("1, Test")))
.andExpect(jsonPath("$.address.zipcode", is("098716")));
}
I really like the second option since it looks cleaner to business users and testers, but on the other hand for a developer that will consume this API the first option looks more visible since it shows the JSON response.
The third option is the easiest one since it's just Java code, but the readability and the cross-team interaction is not as nice as cucumber.

You should use the third option but not with junit, you should do it using spock.This is the best of both the worlds.
Spock tests are written like this
def "description of what you want to test"() {
given:
//Do what is pre-requisite to the test
when:
def response = mockMvc.perform(get("/person/id")).andReturn().getResponse();
then:
checkForResponse.each {
c->c(response )
}
where:
id | checkResponse
1 | [ResponseChecker.correctPersondetails()]
100 | [ResponseChecker.incorrectPersondetails()]
}

Integration test are made to test if components of your application can work together. For example, you test some requests to database and mvc controller with integration tests. Integration tests are here to test your infrastructure.
On the other hand, BDD tests are made to facilitate communication between development and specifications. The common idea is to write tests or specification by example. There are definitely not design to write integration tests.
I would recommend the third option.

Related

How we can run same feature file on multiple browser sequentially? [duplicate]

I am able to execute WebUI feature file against single browser (Zalenium) using parallel runner and defined driver in karate-config.js. How can we execute WebUI feature file against multiple browsers (Zalenium) using parallel runner or distributed testing?
Use a Scenario Outline and the parallel runner. Karate will run each row of an Examples table in parallel. But you will have to move the driver config into the Feature.
Just add a parallel runner to this sample project and try: https://github.com/intuit/karate/tree/master/examples/ui-test
Scenario Outline: <type>
* def webUrlBase = karate.properties['web.url.base']
* configure driver = { type: '#(type)', showDriverLog: true }
* driver webUrlBase + '/page-01'
* match text('#placeholder') == 'Before'
* click('{}Click Me')
* match text('#placeholder') == 'After'
Examples:
| type |
| chrome |
| geckodriver |
There are other ways you can experiment with, here is another pattern when you have a normal Scenario in main.feature - which you can then call later from a Scenario Outline from a separate "special" feature - which is used only when you want to do this kind of parallel-ization of UI tests.
Scenario Outline: <config>
* configure driver = config
* call read('main.feature')
Examples:
| config! |
| { type: 'chromedriver' } |
| { type: 'geckodriver' } |
| { type: 'safaridriver' } |
EDIT - also see this answer: https://stackoverflow.com/a/62325328/143475
And for other ideas: https://stackoverflow.com/a/61685169/143475
EDIT - it is possible to re-use the same browser instance for all tests and the Karate CI regression test does this, which is worth studying for ideas: https://stackoverflow.com/a/66762430/143475

Grails spock test fails with "GroovyCastException: Cannot cast object"

I have a simple class that I'm writing a test for. Something weird happens when I call validate() on an instance of that class, which relates to my custom validator for my date property. If my custom validator calls a static method, everything seems to work fine. However, if I call a non-static method (which I need to as I need to check some other properties of my instance) I get the following error:
Condition failed with Exception:
testDate.validate(['date']) == valid
| |
| org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object 'class hegardt.backend.grails.model.person.TestDate' with class 'java.lang.Class' to class 'hegardt.backend.grails.model.person.TestDate'
| at hegardt.backend.grails.model.person.TestDate._clinit__closure1$_closure2(TestDate.groovy:20)
| at groovy.lang.Closure.call(Closure.java:405)
| at org.grails.datastore.gorm.validation.constraints.ValidatorConstraint.processValidate(ValidatorConstraint.java:100)
| at org.grails.datastore.gorm.validation.constraints.AbstractConstraint.validate(AbstractConstraint.java:88)
| at grails.gorm.validation.DefaultConstrainedProperty.validate(DefaultConstrainedProperty.groovy:601)
| at grails.validation.Validateable$Trait$Helper.doValidate(Validateable.groovy:192)
| at grails.validation.Validateable$Trait$Helper.validate(Validateable.groovy:163)
| at grails.validation.Validateable$Trait$Helper.validate(Validateable.groovy:129)
| at hegardt.backend.grails.model.person.TestDateSpec.test(TestDateSpec.groovy:15)
<hegardt.backend.grails.model.person.TestDate#48bb8764 date=null privateVar=false grails_validation_Validateable__beforeValidateHelper=org.grails.datastore.gorm.support.BeforeValidateHelper#1624fd07 grails_validation_Validateable__errors=grails.validation.ValidationErrors: 0 errors>
Here's my class definition:
#GrailsCompileStatic
class TestDate implements Validateable {
LocalDate date
boolean privateVar
static constraints = {
date nullable: true, validator: {LocalDate val -> validateDate(val)}
}
private validateDate(LocalDate val) { // If this method is static, everything works fine
return privateVar
}
}
and here's my test class
class TestDateSpec extends Specification {
#Unroll
void "test"() {
given:
TestDate testDate = new TestDate(date: date)
expect:
testDate.validate(['date']) == valid // This call fails for all 4 test cases...
where:
date | valid
null | true
LocalDate.now().plusDays(1) | false
LocalDate.now() | false
LocalDate.now() | true
}
}
PS
I have also tried without #GrailsCompileStatic but this just generates a different "cast" related error:
Condition failed with Exception:
testDate.validate(['date']) == valid
| |
| java.lang.IllegalArgumentException: object is not an instance of declaring class
| at org.grails.datastore.gorm.validation.constraints.builder.ConstrainedPropertyBuilder.doInvokeMethod(ConstrainedPropertyBuilder.java:65)
| at groovy.util.BuilderSupport.invokeMethod(BuilderSupport.java:64)
| at groovy.lang.GroovyObjectSupport.invokeMethod(GroovyObjectSupport.java:44)
| at hegardt.backend.grails.model.person.TestDate._clinit__closure1$_closure2(TestDate.groovy:19)
| at groovy.lang.Closure.call(Closure.java:405)
| at org.grails.datastore.gorm.validation.constraints.ValidatorConstraint.processValidate(ValidatorConstraint.java:100)
| at org.grails.datastore.gorm.validation.constraints.AbstractConstraint.validate(AbstractConstraint.java:88)
| at grails.gorm.validation.DefaultConstrainedProperty.validate(DefaultConstrainedProperty.groovy:601)
| at grails.validation.Validateable$Trait$Helper.doValidate(Validateable.groovy:192)
| at grails.validation.Validateable$Trait$Helper.validate(Validateable.groovy:163)
| at grails.validation.Validateable$Trait$Helper.validate(Validateable.groovy:129)
| at hegardt.backend.grails.model.person.TestDateSpec.test(TestDateSpec.groovy:15)
<hegardt.backend.grails.model.person.TestDate#1e742012 date=null privateVar=false grails_validation_Validateable__beforeValidateHelper=org.grails.datastore.gorm.support.BeforeValidateHelper#4f3967c7 grails_validation_Validateable__errors=grails.validation.ValidationErrors: 0 errors>
You are attempting to invoke an instance method (validateDate) from a static context (static constraints block) which is not allowed. You could mark validateDate as static but that moves the problem to privateVar which is an instance variable so you would not be able to reach that from a static method. It isn't clear what role that field plays so it isn't clear how best to deal with that, but the problem described in the question is caused because you are attempting to invoke an instance method from a static context.

How do I get information about geobjects from a geoserver layer using the GetFeatureInfo?

In GeoServer I successfully created WMS layer based on PostgreSQL table which has such structure:
| COLUMN NAME | DATA TYPE |
|-------------|-----------|
| id | numeric |
| geom | geometry |
| city | varchar |
| info | jsonb |
Each record in that table is unique and it's one polygon. In other words, this layer has a lot of polygons. With GetMap WMS request I put this layer to the map of the web application. When the user clicks to one of these polygons I want to know information about it. For example information from city and info columns. As far as I understand I have to make a GetFeatureInfo WMS request for this task, right? I tried such GET request to GeoServer, but it returns me the empty result. What I did wrong?
GET REQUEST:
http://{{domain_name}}/geoserver/{{namespace_name}}/wms?&
SERVICE=WMS&
VERSION=1.3.0&
REQUEST=GetFeatureInfo&
LAYERS={{layer_name}}&
SRS=EPSG%3A4326&
CRS=CRS%3A84&
FORMAT=image%2Fpng8&
BBOX=51.08443921044546%2C71.3090464064941%2C51.18218384993084%2C71.55709709619134&
WIDTH=1366&
HEIGHT=905&
QUERY_LAYERS={{layer_name}}&
INFO_FORMAT=application%2Fjson&
FEATURE_COUNT=50&
I=498&
J=391&
EXCEPTIONS=application%2Fvnd.ogc.se_xml&
STYLES=squaremesh_style
RESULT:
{
"type": "FeatureCollection",
"features": [],
"totalFeatures": "unknown",
"numberReturned": 0,
"timeStamp": "2019-12-24T17:59:23.429Z",
"crs": null
}
It's possible that there is nothing visible at pixel (669, 491) - did you try other points?
However, there are a number of issues with your request (though I think GeoServer takes a lenient view of some of them):
For version 1.3.0 of WMS you should use I and J for the query point.
Your SRS should be EPSG:4326.
You are missing a STYLES parameter
Finally, if all else fails don't be afraid to check the log file to see if an error is being logged.

SpecFlow Step Generation for Scenario Outline Generating Incorrect Methods

I'm new in Visual Studio. I'm using Visual Studio 2015 with SpecFlow. Below is the Feature File:
#mytag
Scenario Outline: Successful Authentication
Given I am a user of type <user>
When I hit the application URL
And I provide <email>
And I click on Log In Button
Then I will be landed to the Home Page
And I will be able to see <name> on the Home Page
Examples:
| user | email | name |
| admin | a.b#example.com | alpha |
| non-admin | b.c#example.com | beta |
When I generate the step definitions I'm expecting parameters in place of the variables, instead the method is generated as below:
[Given(#"I am a user of type admin")]
public void GivenIAmAUserOfTypeAdmin()
{
ScenarioContext.Current.Pending();
}
I was instead expecting a method like:
[Given(#"I am a user of type '(.*)'")]
public void GivenIAmAUserOfType(string p0)
{
ScenarioContext.Current.Pending();
}
What am I missing?
As an example, surrounding the <user> in the Given step with '' like this,
Given I am a user of type '<user>'
will generate the desired results. It's probably needed in order to recognize the regular expression.

Using json_spec gem's 'at "path" should include:' step is not working as I would expect

(apologies in advance for the cucumber steps, they need to be cleaned up a fair bit to flow better)
I am using a combination of Cucumber, along with the rest-client, and json_spec gems to create a test suite for a restful API. The approach is similar to that given in the Cucumber Book Note that in this case, since the 'customer' is a developer, the 'language of the business' is far more technical than you would normally express in cucumber scenarios
I am having a problem with the json_spec cucumber step "Then the JSON at "path" should include:"
My scenario looks like this
Scenario Outline: GET to OR Packages for specific package uuid returns proper data
Given I create a request body from the following JSON data
"""
{
"package":
{
"name": "anothertestpackage",
"description": "This is a test, testing 4 5 6",
"package_type" : <package_type>,
"duration": 30,
"start_date": "2012-03-01T08:00:00Z"
}
}
"""
And I have the URI for a new: package made in: OR from the request body
When I make a: GET request to the URI for: my new package with no query string
Then the JSON at "package" should include:
"""
{
"name": "anothertestpackage",
"description": "This is a test, testing 4 5 6",
"package_type" : <package_type>,
"duration": 30,
"start_date": "2012-03-01T08:00:00Z"
}
"""
Examples:
| package_type |
| "IMPRESSIONS" |
| "CLICKS" |
| "LEADS" |
And the contents of last_json are like this at the point the Then step is executed
{
"package": {
"status": "NEW",
"account": {
"resource_uri": "/api/v0001/accounts/fecdbb85a3584ca59820a321c3c2767d"
},
"name": "anothertestpackage",
"package_type": "IMPRESSIONS",
"margin_goal": "0.5000",
"duration": 30,
"resource_uri": "/api/v0001/packages/fecdbb85a3584ca59820a321c3c2767d/feea333776c9454c92edab8e73628cbd",
"start_date": "2012-03-01T08:00:00Z",
"description": "This is a test, testing 4 5 6"
}
}
I should think the step would pass, but I'm getting this error instead
Expected included JSON at path "package" (RSpec::Expectations::ExpectationNotMetError)
features\OR\API\OR_API_Packages.feature:70:in `Then the JSON at "package" should include:'
It is unclear what that error is telling me in terms of what is wrong. Is this user error? should I be using a different means to determine if the expected key:value pairs are present in the JSON returned by the API? I don't really see any examples of doing this kind of comparison in your feature files for the gem, so it is difficult to know if this is not what include was intended for.
Heh just got an answer from one of the gem authors via another venue. Will post it here
Include was intended more for simple value inclusion, mostly in
arrays. If an API index response returned an array of objects, you
could assert that the array includes one whole, identical object.
Check out the matcher specs for examples.
For what you're doing, I'd break it out into separate steps:
Then the JSON at "package/name" should be "anothertestpackage"
And the JSON at "package/description" should be "This is a test, testing 4 5 6"
And the JSON at "package/package_type" should be <package_type>
And the JSON at "package/duration" should be 30
And the JSON at "package/start_date" should be "2012-03-01T08:00:00Z"
Or you could make use of a table format to make that more succinct
such as
Then the JSON at "package" should have the following:
| name | "anothertestpackage" |
| description | "This is a test, testing 4 5 6" |
| package_type | <package_type> |
| duration | 30 |
| start_date | "2012-03-01T08:00:00Z" |
I hope that helps! Thanks for the question.
Indeed it did help very much, thank you 'laserlemon'

Resources