Image Comparison during Automation

     I had a requirement to compare the images/screenshot of the application during the automation execution. In that use case, I have to inject an expected image and compare it against the application’s UI during the runtime; after that need to share the test results in a detailed report. The report should capture the scenario, expected image, match percentage, time taken for comparison, the image comparison date time, and status.

     I have implemented a utility function compareImages to perform the image comparison and the utility will return a percentage match. Along with compareImages, another function trackDetailsToReport gets an excel report with the scenario, expected image, match percentage, time taken for comparison, the image comparison date time and status.

     In the compareImages function, I used the Selenium driver instances to capture the actual image and used AWT (Abstract Window Toolkit) to read the actual and expected image’s height and width, then checking the number of pixels similarity between the actual and expected image. Finally, calculate the match percentage and send the values to the function trackDetailsToReport for further report generation. Below is the actual implementation of compareImages function:

public String compareImages(WebDriver driver, String scenario, String expectedImageFilePath, String featureName) {
		String matchpercentage = null;
		try {
			BufferedImage image;
			int width = 0;
			int height = 0;
			int[][] clr;
			BufferedImage images;
			int widthe = 0;
			int heighte = 0;
			int[][] clre;
			double start = System.currentTimeMillis();
			Thread.sleep(5);

			File outputfile;
			try {
				outputfile = ((TakesScreenshot) new Augmenter().augment(driver)).getScreenshotAs(OutputType.FILE);
				if (!outputfile.canRead() || !outputfile.isFile()) {
					throw new Exception("Failed to get current screen");
				}
				image = ImageIO.read(outputfile);
				width = image.getWidth(null);
				height = image.getHeight(null);
				clr = new int[width][height];
			} catch (Exception e) {
				throw new Exception("Failed to get current screen");
			}

			try {
				File inFile = new File(expectedImageFilePath).getAbsoluteFile();
				if (!inFile.canRead() || !inFile.isFile()) {
					throw new Exception("Specified expected image not found");
				}
				images = ImageIO.read(inFile);
				widthe = images.getWidth(null);
				heighte = images.getHeight(null);
				clre = new int[widthe][heighte];
			} catch (Exception e) {
				throw new Exception("Specified expected image not found");
			}

			int smw = 0;
			int smh = 0;
			int p = 0;
			// Calculating the smallest value among width and height
			if (width > widthe) {
				smw = widthe;
			} else {
				smw = width;
			}
			if (height > heighte) {
				smh = heighte;
			} else {
				smh = height;
			}
			// Checking the number of pixels similarity
			for (int a = 0; a < smw; a++) {
				for (int b = 0; b < smh; b++) {
					clre[a][b] = images.getRGB(a, b);
					clr[a][b] = image.getRGB(a, b);
					if (clr[a][b] == clre[a][b]) {
						p = p + 1;
					}
				}
			}
			float w, h = 0;
			if (width > widthe) {
				w = width;
			} else {
				w = widthe;
			}
			if (height > heighte) {
				h = height;
			} else {
				h = heighte;
			}
			float s = (smw * smh);
			// Calculating the percentage
			float x = (100 * p) / s;
			matchpercentage = String.valueOf(new DecimalFormat("#").format(x)) + "%";

			DateFormat dateFormat = new SimpleDateFormat("dd-MMM-yyyy_HH-mm-ss");
			Date date = new Date();
			String dateTime = dateFormat.format(date).toString();

			double stop = System.currentTimeMillis();
			String timeTakenForComparison = String.valueOf((stop - start) / 1000);

			// Track the comparison details to the report
			trackDetailsToReport(scenario, expectedImageFilePath.toString(), matchpercentage, timeTakenForComparison,
dateTime, featureName);

		} catch (Exception lException) {
			lException.printStackTrace();
		}
		return matchpercentage;
	} 

     In the above logic, driver is used to capturing the current execution screen, scenario is an argument to display the name of the scenario in the report, expectedImageFilePath is the file path of the expected image to be compared in the logic, and featureName is used to print along with generated report file name to differentiate the feature flow if we have to compare images at different feature flows. You can use the compareImages function in the following syntax:

compareImages(driver, "scenario_name",
 "expected_image_path", "feature_name");

     Next, we will talk about the trackDetailsToReport function. Below is the logic to generate the excel report with the scenario, expected image, match percentage, time taken for comparison, the image comparison date time and status.

private void trackDetailsToReport(String scenario, String expectedImage, String percentMatch,
			String timeTakenForComparison, String dateTime, String featureName) throws IOException {

		// Creating file object of existing excel file
		if (!new File(System.getProperty("user.dir") + "\\Reports").exists()) {
			(new File(System.getProperty("user.dir") + "\\Reports")).mkdir();
		}
		if (!new File(System.getProperty("user.dir") + "\\Reports\\Image_Comparision\\").exists()) {
			new File(new File(System.getProperty("user.dir")), "\\Reports\\Image_Comparision\\").mkdirs();
		}
		DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy_MM_dd");
		LocalDateTime dateNow = LocalDateTime.now();
		String dateValue = dateFormat.format(dateNow);
		String filePath = System.getProperty("user.dir") + "\\Reports\\Image_Comparision\\Image_Comparision_Report_"
				+ featureName + "_" + dateValue + ".xlsx";

		File excelFile = new File(filePath);
		OutputStream fileOut = null;
		Sheet sheet;
		File fileName;

		Workbook wb = new XSSFWorkbook();
		if (!excelFile.exists()) {
			fileName = new File(filePath);
			fileOut = new FileOutputStream(fileName);
			sheet = wb.createSheet("Sheet1");

			// Create headers and set style
			Row header = sheet.createRow(0);
			header.createCell(0).setCellValue("Scenario");
			header.createCell(1).setCellValue("Expected Image");
			header.createCell(2).setCellValue("% Match");
			header.createCell(3).setCellValue("Time Taken for Comparison (in seconds)");
			header.createCell(4).setCellValue("Date Time");
			header.createCell(5).setCellValue("Status");
			CellStyle style = wb.createCellStyle();
			Font font = wb.createFont();
			font.setFontHeightInPoints((short) 12);
			font.setBold(true);
			style.setFont(font);
			style.setWrapText(true);
			sheet.setColumnWidth(0, 70 * 256);
			sheet.setColumnWidth(1, 50 * 256);
			sheet.setColumnWidth(2, 15 * 256);
			sheet.setColumnWidth(3, 25 * 256);
			sheet.setColumnWidth(4, 15 * 256);
			sheet.setColumnWidth(5, 10 * 256);
			for (int j = 0; j <= 5; j++) {
				header.getCell(j).setCellStyle(style);
				CellStyle cellHeaderStyle = header.getCell(j).getCellStyle();
				cellHeaderStyle.setVerticalAlignment(VerticalAlignment.CENTER);
				cellHeaderStyle.setAlignment(HorizontalAlignment.CENTER);
				header.getCell(j).setCellStyle(cellHeaderStyle);
			}
			wb.write(fileOut);
			System.out.println("Report has been created successfully.");
		}
		wb.close();

		// New records to update in the report
		Object[][] newdataLists = { { scenario, expectedImage, percentMatch, timeTakenForComparison, dateTime } };
		try {
			// Creating input stream
			FileInputStream inputStream = new FileInputStream(filePath);

			// Creating workbook from input stream
			Workbook workbook = WorkbookFactory.create(inputStream);

			// Reading first sheet of excel file
			sheet = workbook.getSheetAt(0);

			// Getting the count of existing records
			int rowCount = sheet.getLastRowNum();

			// Iterating new data to update
			for (Object[] data : newdataLists) {

				// Creating new row from the next row count
				Row row = sheet.createRow(++rowCount);

				int columnCount = 0;

				// Iterating data informations
				for (Object info : data) {

					// Creating new cell and setting the value
					Cell cell = row.createCell(columnCount++);
					if (info instanceof String) {
						cell.setCellValue((String) info);
					} else if (info instanceof Integer) {
						cell.setCellValue((Integer) info);
					}
				}
				// Setting the status and also setting the color style
				for (int i = 1; i < sheet.getLastRowNum() + 1; i++) {
					CellStyle style = workbook.createCellStyle();
					Font font = workbook.createFont();
					font.setBold(true);
					DataFormatter df = new DataFormatter();
					String actualPercentage = df.formatCellValue(sheet.getRow(i).getCell(2));
					Cell cell = row.createCell(5);
					if (!actualPercentage.equals("100%")) {
						font.setColor(HSSFColorPredefined.RED.getIndex());
						style.setFont(font);
						cell.setCellStyle(style);
						cell.setCellValue((String) "FAIL");
					} else {
						font.setColor(HSSFColorPredefined.GREEN.getIndex());
						style.setFont(font);
						cell.setCellStyle(style);
						cell.setCellValue((String) "PASS");
					}
				}
			}
			// Close input stream
			inputStream.close();

			// Crating output stream and writing the updated workbook
			FileOutputStream os = new FileOutputStream(filePath);
			workbook.write(os);

			// Close the workbook and output stream
			workbook.close();
			os.close();

			System.out.println("Report has been updated successfully.");

		} catch (EncryptedDocumentException | IOException e) {
			System.err.println("Exception while updating an existing report.");
			e.printStackTrace();
		}
	}

     The above logic will create Image_Comparision folder inside the Reports folder in the project structure, also the image comparison test report will be generated inside Image_Comparision folder. In the report, we can see the details like scenario, expected image, match percentage, time taken for comparison, the image comparison date time and status. The status should be PASS when the match percentage value is 100%. Following is the sample report generated during the runtime,

     I hope you really enjoyed reading this article and you got some implementation logic for dynamic image comparison during your automation script execution. Try to utilize the compareImages function anywhere in your automation test suite wherever you have such image comparison requirements.

make it perfect!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Create a website or blog at WordPress.com

Up ↑

%d bloggers like this: