Generative AI data driven testing, a magical example
AI can either be a boon or a curse. Depends on how we handle it. PC: https://www.monkeyuser.com/

Generative AI data driven testing, a magical example

The wizarding world is undergoing a recession, so they looked upon the muggle world for work. Now the closest thing to magic in the muggle world is AI, so our friends from Hogwarts sought Google Gemini AI's advice to consider where they fit in and made a list. You can watch it below, do watch it till the end 😉.

Accurate? Debatable. Fun? Hell yeah!

There are 4 important tools and their libraries which made this article possible, it's going to be a long read so brace yourself.

1. Google AI Studio:

The google AI studio uses Gemini AI models to generate responses. Gemini might infamously be known to suggest drinking something unusual to avoid kidney stones, but its resilient. While other AI tools have closed their daily quota of free API requests after a few hits, Gemini is able to generate valid responses enough number of times so that I could finish the debugging and make this article possible. You can generate the token needed to access AI Studio through API here. Below is the curl which contains the query I have used to generate the data. Parsing a json felt less harrowing than a csv or a text file, hence my choice.

curl --location 'https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-pro-latest:generateContent?key=enter%20your%20key' \
--header 'Content-Type: application/json' \
--data '{
  "contents": [
    {
      "role": "user",
      "parts": [
        {
          "text": "generate a list of Harry potter characters with their names and single worded corporate job roles in simple json"
        }
      ]
    }
  ],
  "generationConfig": {
    "temperature": 1,
    "topK": 0,
    "topP": 0.95,
    "maxOutputTokens": 8192,
    "stopSequences": []
  },
  "safetySettings": [
    {
      "category": "HARM_CATEGORY_HARASSMENT",
      "threshold": "BLOCK_MEDIUM_AND_ABOVE"
    },
    {
      "category": "HARM_CATEGORY_HATE_SPEECH",
      "threshold": "BLOCK_MEDIUM_AND_ABOVE"
    },
    {
      "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
      "threshold": "BLOCK_MEDIUM_AND_ABOVE"
    },
    {
      "category": "HARM_CATEGORY_DANGEROUS_CONTENT",
      "threshold": "BLOCK_MEDIUM_AND_ABOVE"
    }
  ]
}
'        

There is a crucial benefit of customization, for example if you want your AI's API service to create only 5 entries, you can change the search query to "generate a list of five Harry potter characters with their names and single worded corporate job roles in simple json".

customized request

Talking about API hits, let's check out the second tool.

2. Jackson:

I usually use RestAssured for my API testing, but I have decided to explore Jackson this time, and it turned out to be perfect. Combining it with RestAssured can make the API tests even more robust. Let's check out the code:

a. post API for the above curl request using Jackson. Accio! 💫

	public static Map<String, String> aiDataGenerator() throws IOException {
		String apiKey = "";// your API key
		String url = "https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-pro-latest:generateContent";

		// Create the request body object
		RequestBody requestBody = new RequestBody();
		requestBody.getContents().add(new Content("user", new Part(
				"generate a list of Harry potter characters with their names and single worded corporate job roles in simple json")));
		requestBody.setGenerationConfig(new GenerationConfig(1, 0, 0.95, 8192, new String[] {}));
		requestBody.getSafetySettings().add(new SafetySetting("HARM_CATEGORY_HARASSMENT", "BLOCK_MEDIUM_AND_ABOVE"));
		requestBody.getSafetySettings().add(new SafetySetting("HARM_CATEGORY_HATE_SPEECH", "BLOCK_MEDIUM_AND_ABOVE"));
		requestBody.getSafetySettings()
				.add(new SafetySetting("HARM_CATEGORY_SEXUALLY_EXPLICIT", "BLOCK_MEDIUM_AND_ABOVE"));
		requestBody.getSafetySettings()
				.add(new SafetySetting("HARM_CATEGORY_DANGEROUS_CONTENT", "BLOCK_MEDIUM_AND_ABOVE"));

		// Convert the request body object to JSON
		ObjectMapper mapper = new ObjectMapper();
		mapper.enable(SerializationFeature.INDENT_OUTPUT);
		String requestBodyJson = mapper.writeValueAsString(requestBody);

		// Send the request
		URL requestUrl = new URL(url + "?key=" + apiKey);
		HttpURLConnection connection = (HttpURLConnection) requestUrl.openConnection();
		connection.setRequestMethod("POST");
		connection.setRequestProperty("Content-Type", "application/json");
		connection.setDoOutput(true);

		try (OutputStream os = connection.getOutputStream()) {
			byte[] input = requestBodyJson.getBytes("utf-8");
			os.write(input, 0, input.length);
		}

		int responseCode = connection.getResponseCode();
		System.out.println("Response code: " + responseCode);

		// Read the response
		StringBuilder response = new StringBuilder();
		try (BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(), "utf-8"))) {
			String responseLine = null;
			while ((responseLine = br.readLine()) != null) {
				response.append(responseLine.trim());
			}
		}

		JsonPath jsonPath = new JsonPath(response.toString());

		String json = jsonPath.getString("candidates.content.parts.text");

		// Find the index of the first opening curly brace {
		int startIndex = json.indexOf("{");
		// Find the index of the last closing curly brace }
		int endIndex = json.lastIndexOf("}");

		if (startIndex != -1 && endIndex != -1) {
			// Extract the substring between the first opening and last closing curly braces
			json = json.substring(startIndex, endIndex + 1);
		}

		// Parse JSON string into a list of maps
		Map<String, String> dataList = mapper.readValue(json, Map.class);

		// Convert list of maps into JSON using Jackson
		String jsonArray = mapper.writeValueAsString(dataList);

		// Print the resulting map
		System.out.println(dataList);

		connection.disconnect();
		return dataList;
	}        

The above code is a part of the main class Gemini.java which also contains the methods for building POJO for the request built in the method above. It hits the API and stores the names and roles of the characters and return it and return it as a map. The code looks lengthier as safety settings mentioned by google need to be added. The size of the map generated above will be used to decide the number of rows deleted omitting Seamus Finnigan as he is the showstopper of this article.

Now one can ask me, you have generated the data, good. How are you going to use this data? This brings the third tool.

3. reqres.in: This is one of the friendliest APIs on the internet to practice API testing. It supports CRUD (Create, Read, Update, Delete) actions related to user management. So, we are going to create and delete users in reqres API using the map generated by the aiDataGenerator() method above.

User creation: Erecto! 💫

public static JsonPath reqresPost(Entry<String, String> body) throws ClientProtocolException, IOException {
        // Define the URL
        String url = "https://reqres.in/api/users";
        
        // Create a map for the request body
        Map<String, String> requestBody = new HashMap<>();
        requestBody.put("name", body.getKey());
        requestBody.put("job", body.getValue());
        
            // Create an instance of ObjectMapper
            ObjectMapper objectMapper = new ObjectMapper();

            // Convert the request body map to JSON string
            String requestBodyJson = objectMapper.writeValueAsString(requestBody);

            // Create an instance of HttpClient
            CloseableHttpClient httpClient = HttpClients.createDefault();

            // Create an instance of HttpPost
            HttpPost httpPost = new HttpPost(url);

            // Set the Content-Type header
            httpPost.setHeader("Content-Type", "application/json");

            // Set the request body
            StringEntity stringEntity = new StringEntity(requestBodyJson, ContentType.APPLICATION_JSON);
            httpPost.setEntity(stringEntity);

            // Execute the request
            CloseableHttpResponse response = httpClient.execute(httpPost);

            
         // Extract and print the response body content
            HttpEntity responseEntity = response.getEntity();
            String responseBody = EntityUtils.toString(responseEntity);
            System.out.println(responseBody);
            JsonPath path = new JsonPath(responseBody);
            // Close the HttpClient
            httpClient.close();
 
		return path;
    }        

The above code returns a user ID along with the timestamp of the user creation.

User Deletion: Avada Kedavra! ☠

public static void reqresDelete(String id)
    {
    	// Define the URL
        String url = "https://reqres.in/api/users/"+id;

        try {
            // Create an instance of HttpClient
            CloseableHttpClient httpClient = HttpClients.createDefault();

            // Create an instance of HttpDelete
            HttpDelete httpDelete = new HttpDelete(url);

            // Execute the request
            CloseableHttpResponse response = httpClient.execute(httpDelete);

            int statusCode = response.getStatusLine().getStatusCode();
            if(statusCode==204)
            	System.out.println("Response Status Code: "+statusCode+", success");
            else
            	System.out.println("unable to delete");

            // Close the HttpClient
            httpClient.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }        

The code above belongs to Reqres.java class. In between the user creation and deletion, the data is documented which brings us to the final tool of the article.

4. Google cloud API Library: Google has a vast collection of tools and products. In order to access them via APIs you can use this library. All you have to do is create a project on google cloud, activate the necessary APIs in the library, enable the oauth consent and generate credentials for a desktop app and download the client secret json which can be used for interacting with google API's. You can get a clear idea about the process in this documentation. So, let's have a look at the methods that add and remove rows from the google sheet.

Adding rows: Aparecium! 💫

public static void sheetAppend(List<List<String>> users) throws IOException, GeneralSecurityException {
		Sheets sheetService = getSheetService();
		String range ="employee_list!A2:D11";
		boolean empty = false;
		
		//Reading the data to check if the targeted rows are empty or not
		ValueRange response = sheetService.spreadsheets().values()
				.get(spreadsheetId, range)
				.execute();
		
		List<List<Object>> values = response.getValues();
		if(values == null||values.isEmpty())
		{
			System.out.println("No data found, we can go ahead with writing data");
			empty = true;
			
		}else
		{
			for(List row : values)
			{
				System.out.println(row.get(0)+" "+row.get(1)+" "+row.get(2));
			}
		}
		
		//writing data to excel sheet
		
		for(List<String> user: users)
		{
			System.out.println(user);
			ValueRange appendBody = new ValueRange()
					.setValues(Arrays.asList(Arrays.asList(user.get(0),user.get(1),user.get(2),user.get(3))));
			
			try {
				sheetService.spreadsheets().values().append(spreadsheetId, "employee_list", appendBody)
				.setValueInputOption("USER_ENTERED")
				.setInsertDataOption("INSERT_ROWS")
				.setIncludeValuesInResponse(true)
				.execute();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}	
	}        

Removing rows: Evanesco! 💫

public static void deleteCells(int size) throws IOException, GeneralSecurityException
	{
		Sheets sheetService = getSheetService();
		String range ="employee_list!A2:D"+size;
		
		
		ClearValuesRequest clearValuesRequest = new ClearValuesRequest();
		sheetService.spreadsheets().values().clear(spreadsheetId,range,clearValuesRequest).execute();
		
	}        

depending on the count of the number of users created the number of rows that should be deleted will be calculated. the methods above are available in GoogleSheetsIntegration.java class.

We have reached the end of the article. Hope your experience was magical.

GitHub code link: https://github.com/vinayviga/AutomationSnippets/tree/809591acca1fc70c91d39f9083e7008c51d11434/snippets/GenerativeAI_DataDriven_TestExample

PS:

  1. Some of you might be wondering what are all those words with exclamation marks before every code snippet? Those are spells from Harry potter movies. I tried to mimic the actions performed by the code in those snippets using spells 💫.
  2. I did not optimize the code and put it in a framework because it's the underlying concept I wanted to highlight, so a main method felt sufficient.
  3. In case of any queries or suggestions or even if you like what you've read, do send me an invite or a message 🙂
  4. I could have written my sob story as a post in LinkedIn and ask for job references, however I refrain from that approach. Instead, I share what I have learnt and put forth my effort. Do reach out if you have or know of an opportunity in case you think or feel that I, the test engineer who wrote this, and a few other articles is worth it.

Thank you, and stay tuned for more...

To view or add a comment, sign in

Explore topics