Common Step File for cucumber API Tests - cucumber

 
I am creating an api test automation framework in Typescript using chai and cucumber-js.
 
The API that I am testing has three endoints. I have created Request Spec classes for each of these three endoints as well as a RequestSpecBase class which the endpoint RequestSpec classes inherit from, so that I can reuse common methods e.g. validateHTTPResponse code
 
Assertion method in RequestSpecBaseClass
public validateHttpResponseCode(response: any, expectedHttpCode: number){
    assert.equal(response.statusCode, expectedHttpCode)
}
This is all fine and reducing code duplication, but I do have to have a separate method in each of my step definition files like the ones shown below to call the validation method in the BaseClass.
 
Test step in bulk_lookup_bank_details.steps
  #then(/the bank validation response should return HTTPStatusCode (\d*)/)
  public validateHTTPStatusCode(expectedHTTPStatusCode: number) {
    this.bulkLookupRequestSpec.validateHttpResponseCode(this.response, expectedHTTPStatusCode);
  }
Similar Step repeated in Step def file for other endpoints ..
#then(/the healthcheck response should return a http status code (\d*)/)
  public validateResponse(expectedHttpCode: number) {
    this.healthCheck_Request.validateHttpResponseCode(this.response, expectedHttpCode)
  }
I was wondering if there was a way to use a common steps class. Obviously I would need to pass in the correct instance of whichever Request I am wanting to validate. Is there a better design pattern that I should be adopting?
Much appreciated.

Related

StepVerifier::expectError accepts any exception in Spock

I'm testing a class that uses Spring Boot's webflux library, and I've encountered strange behavior with StepVerifier::expectError. Specifically, I can pass any type (even String!) to the method and the test passes. My method under test should respond with an error Mono for this particular test, and the mono should contain a custom exception. My understanding from this SO question is that I have the StepVerifier operating in the correct block. What is going wrong here?
Class under test:
#Service
#RequiredArgsConstructor
public class PaymentsBO {
private final ContractClient contractClient;
public Mono<Void> updatePaymentInfo(Request record) {
return contractClient
.getContract(UUID.fromString(record.getContractUuid()))
.onErrorResume(throwable -> Mono.error(() -> new CustomException(
"Contract Service responded with a non-200 due to "
+ throwable.getCause())))
.flatMap(
// happy-path logic
);
}
}
Unit test:
def "Returns an error if the Contract Service returns a non-200"() {
given:
def testSubject = new PaymentsBO(contractServiceMock)
def contractServiceMock = Mock(ContractClient)
when:
def result = testSubject.updatePaymentInfo(record)
and:
StepVerifier.create(result)
.expectError(String.class)
then:
1 * contractServiceMock.getContract(CONTRACT_UUID) >> Mono.error(new ContractServiceException())
}
In StepVerifier docs we can read that verification must be triggered by calling one ot the verify methods
Trigger the verification of the resulting StepVerifier on its Publisher using either verify() or verify(Duration). (note some of the terminal expectations above have a "verify" prefixed alternative that both declare the expectation and trigger the verification).
https://projectreactor.io/docs/test/release/api/reactor/test/StepVerifier.html
Your code doesn't use a verify method.
Please consider these two cases:
#Test
void without_verify() {
Mono.error(new IllegalArgumentException(""))
.as(StepVerifier::create)
.expectError(NullPointerException.class);
}
#Test
void with_verify() {
Mono.error(new IllegalArgumentException(""))
.as(StepVerifier::create)
.expectError(NullPointerException.class)
.verify();
}
without_verify is passing because no verification has been triggered.
with_verify is failing because verification has been triggered.

Mocking a public method that has a private method call inside using Mockito (not Powermockito)

I am testing for methodA() in the class :
class Test{
public String methodA(String str){
...
methodB("s1","s2");
...
}
private String methodB(String s1, String s2){
...
}
}
What I have tried in my test class:
Method methodBReflect = Test.class.getDeclaredMethod("methodB", Test.class,String.class, String.class);
methodBReflect.setAccessible(true);
Test testForSpy=new Test();
Test spyTest=spy(testForSpy);
doReturn("return string").when(spyTest..)... // <-- What should be done here?
//methodBReflect.invoke(spyTest, "args1","args2");
P.S: I don't need a solution using powermock as I have some organizational constraints using it.
You shouldn't be testing that method A calls method B. You should test that method A does what method A is supposed to do - that is, it gives the correct output for whatever input you give it. Your test shouldn't care whether method A works by calling method B, or by doing all its processing inline.
Therefore, there's no need to mock or stub anything. Your tests will consist of a bunch of calls to method A, along with verification that the output is what you expect.
You only need to use Mockito (or some other mocking framework) when you're testing a class that interacts with another class entirely; not when the class under test interacts with itself.

Cucumber doesn't generate steps properly

I'm very new to BDD and cucumber testing. I have tried to combine BDD cucumber testing with Rest Assured and I'm facing some issues and need your help
I have a very simple feature file
Feature: End to end test for add category feature
Scenario Outline:Successful addition of a new category
When I try to add a category with request file <requestFile>
Then I receive HTTP status <status>
And The response matches response from file <responseFile>
Examples:
| requestFile | status | responseFile
| "json-files/category/requests/category.json" | 201 | "json-files/common/expected-responses/requestSuccessfullyProcessed.json"
| "json-files/category/requests/category-name-missing.json" | 400 | "json-files/category/expected-responses/category-name-missing.json"
My plan is very simple. I have a pair of a request and a response file and I'm performing end-to-end regression testing.
It's throwing an error in the last step where it's supposed to validate the response file saying step undefined. When I uncommented my step definition methods and tried to let cucumber generate the steps I noticed that this is what it is generating
Step undefined
You can implement this step and 2 other step(s) using the snippet(s) below:
#When("I try to add a category with request file {string}")
public void i_try_to_add_a_category_with_request_file(String string) {
// Write code here that turns the phrase above into concrete actions
throw new io.cucumber.java.PendingException();
}
#Then("I receive HTTP status {int}")
public void i_receive_http_status(Integer int1) {
// Write code here that turns the phrase above into concrete actions
throw new io.cucumber.java.PendingException();
}
#Then("The response matches response from file <responseFile>")
public void the_response_matches_response_from_file_response_file() {
// Write code here that turns the phrase above into concrete actions
throw new io.cucumber.java.PendingException();
}
Notice that the last method somehow didn't generate the {string} argument and this is the reason even when I define the steps, it's not working.
I have no clue if I have made any mistakes in the feature file. A bit of help would be great

"No tests found for given includes" when using TestNG with Groovy

I'm trying to use Test NG with Groovy. I implemented a simple test class, but whenever I try to run it I get the following error:
No tests found for given includes: [tests.OTP.get]
Here is what the tests.OTP class looks like:
class OTP{
public static Logger log = LogManager.getLogger(HomePage.class.getName())
String environment = 'qatesting'
String number = '0769878787'
#Test
def get(){
// SoapuiOTP s = new SoapuiOTP(environment,number)
// s.getOTP()
log.info("hello")
Assert.assertEquals(0,System.getProperty("SOAPUI_OTP"))
log.info System.getProperty('SOAPUI_OTP')
}
}
I use the play button (IntelliJ) next to the def get() to run the test but i get the same error on class level. Please assist, I've tried invaliding my cache and restarting. I have looked at TestNG No tests found. Nothing was run and
TestNG - no tests were found, but it didn't help.
You need to replace def with void in the method annotated with #Test. As the documentation says:
Test methods are annotated with #Test. Methods annotated with #Test that happen to return a value will be ignored, unless you set allow-return-values to true in your testng.xml:
<test allow-return-values="true">
Source: https://testng.org/doc/documentation-main.html#test-methods
The def return type compiles to Object, not void, so TestNG ignores this method by default.
This misconception is not TestNG specific and can be done in JUnit as well: Running Groovy test cases with JUnit 5

Defining Spock mock behaviors

I am writing my first Spock test and read the docs on mocking interactions, but am still not seeing the "forest through the trees" on a few items.
I have a class, MyRealm, that performs authentication for my app. It has two dependencies, AuthService and ShiroAdapter. The former I'd like to mock and the latter I want to leave as-is (if at all possible). This is because the AuthService actually makes a backend connection to LDAP, so I want to mock it. But the ShiroAdapter just defines several utility methods that convert my objects into Apache Shiro security objects (principals, permissions, etc.). So it can be left un-mocked (methinks).
class MyRealmSpec extends Specification {
MyRealm realm
def setup() {
AuthService authService = Mock(AuthService)
// configure 'authService' mock <-- ?????
ShiroAdapter shiroAdapter = new ShiroAdapter()
realm = new MyRealm(authService: authService,
shiroAdapter: shiroAdapter)
}
def "authenticate throws ShiroException whenever auth fails"() {
when:
realm.authenticate('invalid_username', 'invalid_password')
then:
Throwable throwable = thrown()
ShiroException.isAssignableFrom(throwable)
}
}
I believe I'm very close, but am struggling to configure the mock to behave the way I want it to for the test. The Spock docs (linked above) only seem to document how to verify the number of times a mock method is called. I'm not interested in that here.
Here, MyRealm#authenticate(String,String) calls AuthService#doAuth(String,String) under the hood. So I need my mock AuthService instance to simply either return false (indicating failed auth) or to throw an ServiceFaulException if something unexpected happened.
Any ideas how I can accomplish this?
You are very close, an easy, short-hand way to check a thrown exception type is to put the Exception class in parenthesis. Ex:
def "authenticate throws ShiroException whenever auth fails"() {
when:
realm.authenticate('invalid_username', 'invalid_password')
then:
thrown(ShiroException)
}
You also need to mock the LDAP service call itself and simulate an exception or a failed login. The mock operations go in the then clause of your test.
def "authenticate throws ShiroException whenever auth fails"() {
setup:
String invalidUserName = 'invalid_username'
String invalidPassword = 'invalid_password'
when:
realm.authenticate(invalidUserName, invalidPassword)
then:
1 * authService.doAuth(invalidUserName, invalidPassword) >> returnClosure
thrown(ShiroException)
where:
returnClosure << [{throw new ShiroException()}, { false }]
}
Note that you will need to have the arguments on the mock statements match or use wild card matching.
To match on any String you can use the underscore syntax:
1 * authService.doAuth(_, _) >> false
There are a few different behavior objects you might be interested in.
Stub - You only define what gets returned
MyObject obj = Stub{method >> null}
Mocks - You define what gets returned and/or how many times a method is called
MyObject obj = Mock {1..3 methodCall >> false}
Spies - It creates your object but you can override specific methods as a mock (and your overrides can still make calls to the original code)
MyObject obj = Spy {methodCall >> false}
obj.otherMethodCall() // Calls code like normal
obj.methodCall() // Returns false like we told it to
It sounds like you need a stub, but you could use a mock without any issue. I mention spy, because it's a life-saver if your object ever is self dependent (in the future).
def "authenticate throws ShiroException whenever auth fails"() {
given:
AuthService authService = Stub(AuthService)
authService.doAuth(_,_) >> expectedError
MyRealm realm = new MyRealm(
authService: authService,
shiroAdapter: new ShiroAdapter())
when:
realm.authenticate("just enough to get", "to the doAuth method")
then:
thrown(ShiroException)
where:
expectedError << [ShiroException, /*other exceptions this method has to test*/]
}
The data/logic separation isn't needed, but it's a good approach for making tests more flexible and maintainable. Although in this case it's not really needed since you only have one exception to throw.
I would actually separate your failed authentication and exception-ed authentication tests. They're looking at fundamentally different behaviors and the test logic to test both situations is somewhat different. In the interest of maintainability/flexibility, it's in your interest to avoid testing too much (or too little) with each test.
def "authenticate throws ShiroException whenever auth fails"() {
given:
AuthService authService = Stub(AuthService)
authService.doAuth(_,_) >> { args ->
return args[0] == good && args[1] == good
}
MyRealm realm = new MyRealm(
authService: authService,
shiroAdapter: new ShiroAdapter())
expect:
realm.authenticate(username, password) == expectedAuthentication
where:
userName | password | expectedAuthentication
bad | good | false
bad | bad | false
good | good | true
}
Note on the above test, this tests...
The mock's computation for a return value (testing the test)
Any code that happens between calling authenticate and doAuth()
Hopefully that's what you intend. If there's nothing in .authenticate()'s logic that could break (it has complexity on par with a getter or setter method), this test is mostly a waste of time. The only way that logic could break is if something went wrong in the JVM (which is completely outside the responsibility of this test), or someone made a change sometime in the future (ok, even under the huge assumption that .authenticate() contains unbreakably basic logic the test has some value). My rambling off-topic point (terribly sorry); make sure to keep the what & why of your tests in mind. It will help you prioritize test cases and while working out the best ways to organize/separate test logic.

Resources