Store generated dynamic unique ID and parse to next test case - rest

I have keyword groovy which allowed me to generate a dynamic unique ID for test data purpose.
package kw
import java.text.SimpleDateFormat
import com.kms.katalon.core.annotation.Keyword
class dynamicId {
//TIME STAMP
String timeStamp() {
return new SimpleDateFormat('ddMMyyyyhhmmss').format(new Date())
}
//Generate Random Number
Integer getRandomNumber(int min, int max) {
return ((Math.floor(Math.random() * ((max - min) + 1))) as int) + min
}
/**
* Generate a unique key and return value to service
*/
#Keyword
String getUniqueId() {
String prodName = (Integer.toString(getRandomNumber(1, 99))) + timeStamp()
return prodName
}
}
Then I have a couple of API test cases as below:
test case 1:
POST test data by calling the keyword. this test case works well.
the dynamic unique ID is being posted and stored in the Database.
partial test case
//test data using dynamic Id
NewId = CustomKeywords.'kw.dynamicId.getUniqueId'()
println('....DO' + NewId)
GlobalVariable.DynamicId = NewId
//test data to simulate Ingest Service sends Dispense Order to Dispense Order Service.
def incomingDOInfo = '{"Operation":"Add","Msg":{"id":"'+GlobalVariable.DynamicId+'"}'
now, test case 2 served as a verification test case.
where I need to verify the dynamic unique ID can be retrieved by GET API (GET back data by ID, this ID should matched the one being POSTED).
how do I store the generated dynamic unique ID once generated from test case 1?
i have the "println('....DO' + NewId)" in Test Case 1, but i have no idea how to use it and put it in test case 2.
which method should I use to get back the generated dynamic unique ID?
updated Test Case 2 with the suggestion, it works well.
def dispenseOrderId = GlobalVariable.DynamicId
'Check data'
getDispenseOrder(dispenseOrderId)
def getDispenseOrder(def dispenseOrderId){
def response = WS.sendRequestAndVerify(findTestObject('Object Repository/Web Service Request/ApiDispenseorderByDispenseOrderIdGet', [('dispenseOrderId') : dispenseOrderId, ('SiteHostName') : GlobalVariable.SiteHostName, , ('SitePort') : GlobalVariable.SitePort]))
println(response.statusCode)
println(response.responseText)
WS.verifyResponseStatusCode(response, 200)
println(response.responseText)
//convert to json format and verify result
def dojson = new JsonSlurper().parseText(new String(response.responseText))
println('response text: \n' + JsonOutput.prettyPrint(JsonOutput.toJson(dojson)))
assertThat(dojson.dispenseOrderId).isEqualTo(dispenseOrderId)
assertThat(dojson.state).isEqualTo("NEW")
}
====================
updated post to try #2 suggestion, works
TC2
//retrieve the dynamic ID generated at previous test case
def file = new File("C:/DynamicId.txt")
//Modify this to match test data at test case "IncomingDOFromIngest"
def dispenseOrderId = file.text
'Check posted DO data from DO service'
getDispenseOrder(dispenseOrderId)
def getDispenseOrder(def dispenseOrderId){
def response = WS.sendRequestAndVerify(findTestObject('Object Repository/Web Service Request/ApiDispenseorderByDispenseOrderIdGet', [('dispenseOrderId') : dispenseOrderId, ('SiteHostName') : GlobalVariable.SiteHostName, , ('SitePort') : GlobalVariable.SitePort]))
println(response.statusCode)
println(response.responseText)
WS.verifyResponseStatusCode(response, 200)
println(response.responseText)
}

There are multiple ways of doing that that I can think of.
1. Store the value od dynamic ID in a GlobalVariable
If you are running Test Case 1 (TC1) and TC2 in a test suite, you can use the global variable for inter-storage.
You are already doing this in the TC1:
GlobalVariable.DynamicId = NewId
Now, this will only work if TC1 and TC2 are running as a part of the same test suite. That is because GlobalVariables are reset to default on the teardown of the test suite or the teardown of a test case when a single test case is run.
Let us say you retrieved the GET response and put it in a response variable.
assert response.equals(GlobalVariable.DynamicId)
2. Store the value od dynamic ID on the filesystem
This method will work even if you run the test cases separately (i.e. not in a test suite).
You can use file system for permanently storing the ID value to a file. There are various Groovy mehods to help you with that.
Here's an example on how to store the ID to a text file c:/path-to/variable.txt:
def file = new File("c:/path-to/variable.txt")
file.newWriter().withWriter { it << NewID }
println file.text
The TC2 needs this assertion (adjust according to your needs):
def file = new File("c:/path-to/variable.txt")
assert response.equals(file.text)
Make sure you defined file in TC2, as well.
3. Return the ID value at the end of TC1 and use it as an input to TC2
This also presupposes TC1 and TC2 are in the same test suite. You return the value of the ID with
return NewId
and then use as an input parameter for TC2.
4. Use test listeners
This is the same thing as the first solution, you just use test listeners to create a temporary holding variable that will be active during the test suite run.

Related

Function in pytest file works only with hard coded values

I have the below test_dss.py file which is used for pytest:
import dataikuapi
import pytest
def setup_list():
client = dataikuapi.DSSClient("{DSS_URL}", "{APY_KEY}")
client._session.verify = False
project = client.get_project("{DSS_PROJECT}")
# Check that there is at least one scenario TEST_XXXXX & that all test scenarios pass
scenarios = project.list_scenarios()
scenarios_filter = [obj for obj in scenarios if obj["name"].startswith("TEST")]
return scenarios_filter
def test_check_scenario_exist():
assert len(setup_list()) > 0, "You need at least one test scenario (name starts with 'TEST_')"
#pytest.mark.parametrize("scenario", setup_list())
def test_scenario_run(scenario, params):
client = dataikuapi.DSSClient(params['host'], params['api'])
client._session.verify = False
project = client.get_project(params['project'])
scenario_id = scenario["id"]
print("Executing scenario ", scenario["name"])
scenario_result = project.get_scenario(scenario_id).run_and_wait()
assert scenario_result.get_details()["scenarioRun"]["result"]["outcome"] == "SUCCESS", "test " + scenario[
"name"] + " failed"
My issue is with setup_list function, which able to get only hard coded values for {DSS_URL}, {APY_KEY}, {PROJECT}. I'm not able to use PARAMS or other method like in test_scenario_run
any idea how I can pass the PARAMS also to this function?
The parameters in the mark.parametrize marker are read at load time, where the information about the config parameters is not yet available. Therefore you have to parametrize the test at runtime, where you have access to the configuration.
This can be done in pytest_generate_tests (which can live in your test module):
#pytest.hookimpl
def pytest_generate_tests(metafunc):
if "scenario" in metafunc.fixturenames:
host = metafunc.config.getoption('--host')
api = metafuc.config.getoption('--api')
project = metafuc.config.getoption('--project')
metafunc.parametrize("scenario", setup_list(host, api, project))
This implies that your setup_list function takes these parameters:
def setup_list(host, api, project):
client = dataikuapi.DSSClient(host, api)
client._session.verify = False
project = client.get_project(project)
...
And your test just looks like this (without the parametrize marker, as the parametrization is now done in pytest_generate_tests):
def test_scenario_run(scenario, params):
scenario_id = scenario["id"]
...
The parametrization is now done at run-time, so it behaves the same as if you had placed a parametrize marker in the test.
And the other test that tests setup_list now has also to use the params fixture to get the needed arguments:
def test_check_scenario_exist(params):
assert len(setup_list(params["host"], params["api"], params["project"])) > 0,
"You need at least ..."

need help in understanding if the way I am testing a function is correct

I have written this function which is called when a user clicks a link. The function basically creates a copy of the user data with one field altered (thus keeping the original value unchanged i.e. not-mutable) and then updates the database with the new value
def confirmSignupforUser(user:User):Future[Option[User]] = {
println("confirming user: "+user)
val newInternalProfile = user.profile.internalProfileDetails.get.copy(confirmed=true)//new data which should be added in the database
println("old internal profile: "+user.profile.internalProfileDetails.get)
println("new internal profile: "+newInternalProfile)
val newProfile = UserProfile(Some(newInternalProfile),user.profile.externalProfileDetails)
println("old profile: "+user.profile)
println("new profile: "+newProfile)
val confirmedUser = user.copy(profile=newProfile)
for(userOption <- userRepo.update(confirmedUser)) yield { //database operation
println("returning modified user:"+userOption)
userOption
}
}
To test the code, I have written the following spec
"confirmSignupforUser" should {
"change confirmed status to True" in {
val testEnv = new TestEnv(components.configuration)
val externalProfile = testEnv.externalUserProfile
val internalUnconfirmedProfile = InternalUserProfile(testEnv.loginInfo,1,false,None)
val internalConfirmedProfile = internalUnconfirmedProfile.copy(confirmed=true)
val unconfirmedProfile = UserProfile(Some(internalUnconfirmedProfile),externalProfile)
val confirmedProfile = UserProfile(Some(internalConfirmedProfile),externalProfile)
val origUser = User(testEnv.mockHelperMethods.getUniqueID(),unconfirmedProfile)
val confirmedUser = origUser.copy(profile = confirmedProfile)
//the argument passed to update is part of test. The function confirmSignupforUser should pass a confirmed profile
when(testEnv.mockUserRepository.update(confirmedUser)).thenReturn(Future{Some(confirmedUser)})
//// await is from play.api.test.FutureAwaits
val updatedUserOption:Option[User] = await[Option[User]](testEnv.controller.confirmSignupforUser(origUser))
println(s"received updated user option ${updatedUserOption}")
updatedUserOption mustBe Some(confirmedUser)
}
}
I am not confident if I am testing the method correctly. The only way I can check that the confirmed field got changed is by looking at the return value of confirmSignupforUser. But I am actually mocking the value and I have already set the field confirmed to true in the mocked value (when(testEnv.mockUserRepository.update(confirmedUser)).thenReturn(Future{Some(confirmedUser)}).
I know the code works because in the above mock, the update method expects confirmedUser or in other words, a user with confirmed field set to true. So if my code wasn't working, update would have been called with user whose confirmed field was false and mockito would have failed.
Is this the right way to test the method or is there a better way?
You don't need to intialize internalConfirmedProfile in your test. The whole point is to start with confirmed=false, run the confirmSignupforUser method, and make sure that the output is confirmed=true.
You should check 2 things:
check that the return value has confirmed=true (which you do)
check that the repository has that user saved with confirmed=true (which you don't check). To check that you would need to load the user back from the repository at the end.

How to check if an argument of an object is in an ArrayBuffer filled with objects in Scala?

I want to check if an Int typed by an user exists as primary argument/variable(the class has only one argument of type Int) in an ArrayBuffer filled with objects of a class.
It's an assignment for school, and I'm not able to get help of a prof or assistant so I'm asking for your help. I have to code a class "ewallet", which has attributes like "client id"(Int) and "pin code" (random Int between two Numbers). I'm not using "pin code" as an argument of the class as it seems to be implied in the assignment as it says "It has to show the pass when the user creates an account using his client id". I shouldn't be able to create 2 ewallets with the same client id.
So I would have to refuse to create an ewallet when if an ewallet with the same client id exists already in the ArrayBuffer (or array) that stocks ewallets.
Because of the Randomly generated "pin code", it creates different ewallets with same client id and different pin codes. So how to not add an ewallet(class) to a list if an ewallet with some argument exists already?
Thanks for helping a newbie.
I could just created another array list saving the client id's entered by an user and save them to compare with future client id's. But I'd like to learn how to do with an array containing objects.
I tried with for (i <- ListClients) if (NumClient != i.id) but if the list is empty, it doesn't do anything...
class ewallet(clientID: Int){
val id = clientID
val pass = 100 + Random.nextInt((99999-100)+1) //for the class, I didnt include the rest as its not related.
//for the main
def main(args: Array[String]): Unit = {
var ListClients = ArrayBuffer[ewallet]()
var action: Char = " ".charAt(0)
do {
println("[c]-Create ewallet. \n[a]-Access ewallet.\n[q]-Quit")
action = StdIn.readChar()
if (action == 'c') {
val NumClient = StdIn.readLine("Enter your client id :").toInt
var newClient = new ewallet(NumClient)
for (i <- ListClients) if (NumClient == i.id) {
println("Impossible, already exists.")
}
for (i <- ListClients) if (NumClient != i.id){
ListClients += newClient
println("Your pin code is : " + newClient.pass)
}
}
println(ListClients.mkString("\n"))
} while(action != 'q')
I should be able to add newClient to ListClients if NumClient != i.id, but because it's initially empty, it doesn't read those lines… So it does Nothing.
The simplest way would be to assign the result of the presence check to a variable, and add or "not add" the client object based on this variable. In Scala, the idiomatic way to work with collections are their rich set of transformation methods; in your case, exists provides the solution:
val alreadyExists = ListClients.exists(_.id == NumClient)
if (alreadyExists) {
println("Impossible, already exists.")
} else {
ListClients += newClient
println("Your pin code is : " + newClient.pass)
}
If you don't know yet about collection transformations and higher-order functions in general (and you should! they make life a lot easier), then the above piece is actually equivalent to this:
var alreadyExists = false
for (i <- ListClients) {
alreadyExists = alreadyExists || i.id == NumClient
}
(albeit a bit more efficient since it stops the iteration right after the existing client is found, if it is at all present).
As an unrelated comment, in Scala it is conventional to name variables in pascalCase and types in CamelCase. Thus, your class should be called something like EWallet, and variables like ListClients should be called listClients. Also, " ".charAt(0) is the same as ' '.

How to return multiple variables from one test case to another using Katalon

TC1: 01_UserManagement/Login
String u = WebUI.getAttribute(findTestObject('SignInPage/txt_username'), 'placeholder')
WebUI.setText(findTestObject('SignInPage/txt_username'), username)
String p = WebUI.getAttribute(findTestObject('SignInPage/txt_password'), 'placeholder')
CustomKeywords.'com.fcm.utilities.ClearTextField.ClearText'(findTestObject('SignInPage/txt_password'))
WebUI.setText(findTestObject('SignInPage/txt_password'), password)
WebUI.click(findTestObject('SignInPage/btn_signinButton'))
Map map = [:]
map.put('inlinetextofusername',u)
map.put('inlinetextofpassword',p)
map.each{ k, v -> println "${k}:${v}" }
return map;
TestCase2:
Map TC_1_called = WebUI.callTestCase(findTestCase('01_UserManagement/Login'), [('username') : 'Anna', ('password') : 'Analyst_2017',('inlinetextofusername'):'',('inlinetextofpassword'):''],
FailureHandling.STOP_ON_FAILURE)
println(TC_1_called[inlinetextofusername])
println(TC_1_called[inlinetextofpassword]
I am getting following error:-
12-11-2017 04:31:40 PM - [ERROR] - Test Cases/01_UserManagement/Logout FAILED because (of) Variable 'inlinetextofusername' is not defined for test case.
How to take the values which is stored in Map in Test case 1 and to use in Test Case 2.
Map TC_1_called = WebUI.callTestCase(findTestCase('01_UserManagement/Login'), [('username') : 'Anna', ('password') : 'Analyst_2017',('map'):''],
FailureHandling.STOP_ON_FAILURE)
This returns values..
If you want to pass values from one test case to another in Katalon Studio, you should pass these values from Test Case A to Test Case B
Imagine you have these test cases Log in test and Dashboard test and you need to pass username and email from Log in testto Dashboard test
Go to script mode in Log in test and at the end of script add this line of code
WebUI.callTestCase( findTestCase('Test Cases/dashboard test') , [('username'): username, ('email'):email] )
This will pass local variables username and email from Log in test script to dashboard test
Notice: before applying this method, you should create test case variables to the second test case dashboard test from variables tab beside script tab in the lower section of test case editor.
Hope this comes in handy
I have issue on passing variable that has a random value to another test case.
Solution: I created a Global Variable with empty string then I set that Global Variable on my testCase_1 with a random value. I can now call that Global Variable to my testCase_2 without calling testCase_1. But you must execute testCase_1 first on your test suite.
testCase_1:
GlobalVariable.randomString = yourRandomValue
testCase_2:
String variableFromTestCase_1 = GlobalVariable.randomString
Screenshot

Trigger works but test doesn't cover 75% of the code

I have a trigger which works in the sandbox. The workflow checks the field in the campaign level and compares it with the custom setting. If it matches, then it returns the target to the DS Multiplier field. The trigger looks as follows
trigger PopulateTarget on Campaign (before insert, before update)
{
for(Campaign campaign : Trigger.new)
{
if (String.isNotBlank(campaign.Apex_Calculator__c) == true)
{
DSTargets__c targetInstance = DSTargets__c.getInstance(campaign.Apex_Calculator__c);
{
String target = targetInstance .Target__c;
campaign.DS_Target_Multiplier__c = Target;
}
}
}
}
However, I had problems to write a proper test to this and asked for the help on the internet. I received the test
#isTest
private class testPopulateTarget{
static testMethod void testMethod1(){
// Load the Custom Settings
DSTargets__c testSetting = new DSTargets__c(Name='Africa - 10 Weeks; CW 10',Target__c='0.1538', SetupOwnerId = apexCalculatorUserId);
insert testSetting;
// Create Campaign. Since it would execute trigger, put it in start and stoptests
Test.startTest();
Campaign testCamp = new Campaign();
// populate all reqd. fields.
testCamp.Name = 'test DS campaign';
testCamp.RecordTypeId = '012200000001b3v';
testCamp.Started_Campaign_weeks_before_Event__c = '12 Weeks';
testCamp.ParentId= '701g0000000EZRk';
insert testCamp;
Test.stopTest();
testCamp = [Select ID,Apex_Calculator__c,DS_Target_Multiplier__c from Campaign where Id = :testCamp.Id];
system.assertEquals(testCamp.DS_Target_Multiplier__c,testSetting.Target__c);// assert that target is populated right
}
}
Such test returns the error "Compile Error: Variable does not exist: apexCalculatorUserId at line 6 column 122". If I remove that ApexCalculator part System.assertEquals then the test passes. However it covers 4/6 part of the code (which is 66%)
Could anyone help me how should I amend the code to make the coverage of 75%?
Yes, apexCalculatorUserId has not been defined. The code you were given appears to be incomplete. You'll need to look at the constructor DSTargets__c and see what kind of ID it is expecting there.
At a guess, you could try UserInfo.getUserId() to get the ID of the current user, but that may not be the ID that's expected in the constructor. It would be worth trying it to see if the test coverage improves.
1) Replace apexCalculatorUserId with UserInfo.getUserId()
2) I'm not sure what kind of field is Apex_Calculator__c on campaign. If its not a formula you want to insert a new line before "insert testCamp". Something like:
testCamp.Apex_Calculator__c = UserInfo.getUserId();