Close Menu
    DevStackTipsDevStackTips
    • Home
    • News & Updates
      1. Tech & Work
      2. View All

      CodeSOD: Classic WTF: When it’s OK to GOTO

      June 25, 2025

      Overture Maps launches GERS, a system of unique IDs for global geospatial entities

      June 25, 2025

      Agent Mode for Gemini added to Android Studio

      June 24, 2025

      Google’s Agent2Agent protocol finds new home at the Linux Foundation

      June 23, 2025

      Microsoft is reportedly planning yet more major cuts at Xbox — as early as next week

      June 24, 2025

      Microsoft makes Windows 10 security updates FREE for an extra year — but there’s a catch, and you might not like it

      June 24, 2025

      “Deus Ex” just turned 25 years old and it’s still the best PC game of all time — you only need $2 to play it on practically anything

      June 24, 2025

      Where to buy a Meta Quest 3S Xbox Edition — and why it’s a better bargain than the “normal” Meta Quest 3S

      June 24, 2025
    • Development
      1. Algorithms & Data Structures
      2. Artificial Intelligence
      3. Back-End Development
      4. Databases
      5. Front-End Development
      6. Libraries & Frameworks
      7. Machine Learning
      8. Security
      9. Software Engineering
      10. Tools & IDEs
      11. Web Design
      12. Web Development
      13. Web Security
      14. Programming Languages
        • PHP
        • JavaScript
      Featured

      Generate awesome open graph images with Open Graphy

      June 25, 2025
      Recent

      Generate awesome open graph images with Open Graphy

      June 25, 2025

      Defining a Dedicated Query Builder in Laravel 12 With PHP Attributes

      June 25, 2025

      pxlrbt/filament-activity-log

      June 25, 2025
    • Operating Systems
      1. Windows
      2. Linux
      3. macOS
      Featured

      Linux Jargon Buster: What are Secure Boot & Shim Files?

      June 25, 2025
      Recent

      Linux Jargon Buster: What are Secure Boot & Shim Files?

      June 25, 2025

      Fldigi – modem program for most of the digital modes used by radio amateurs

      June 25, 2025

      Lwan is an experimental, scalable, high performance HTTP server

      June 25, 2025
    • Learning Resources
      • Books
      • Cheatsheets
      • Tutorials & Guides
    Home»Development»How to Build a Custom Visual Testing Tool with Selenium: A Practical Guide

    How to Build a Custom Visual Testing Tool with Selenium: A Practical Guide

    June 25, 2025

    Introduction

    Visual testing is crucial for ensuring UI consistency across releases. While commercial tools exist, sometimes you need a custom solution tailored to your specific needs. This guide walks you through creating your own visual testing tool using Selenium and Python.

    Table of Contents

    1. Introduction

    2. Why Build a Custom Visual Testing Tool?

    3. Core Components

    4. Implementation Guide

    5. Advanced Features

    6. Integration & Scaling

    7. Reporting & Analysis

    8. Common Challenges & Solutions

    9. Limitations & Considerations

    10. Conclusion & Next Steps


    Why Build a Custom Solution?

    • Specific requirements not met by commercial tools

    • Cost savings for simple projects

    • Complete control over comparison logic

    • Learning opportunity about image processing

    Core Components

    1. Screenshot Capture with Selenium

    python


    from selenium import webdriver
    import os
    
    def capture_screenshot(url, filename):
        """Capture screenshot of a webpage"""
        driver = webdriver.Chrome()
        driver.get(url)
        
        # Ensure screenshot directory exists
        os.makedirs("screenshots", exist_ok=True)
        screenshot_path = f"screenshots/{filename}"
        
        driver.save_screenshot(screenshot_path)
        driver.quit()
        return screenshot_path

    2. Image Comparison Engine

    python


    from PIL import Image, ImageChops
    import math
    
    def compare_images(baseline_path, current_path, diff_path=None, threshold=0.95):
        """
        Compare two images with similarity threshold
        Returns: (is_similar, similarity_score)
        """
        baseline = Image.open(baseline_path).convert('RGB')
        current = Image.open(current_path).convert('RGB')
        
        # Check dimensions
        if baseline.size != current.size:
            return False, 0
        
        # Calculate difference
        diff = ImageChops.difference(baseline, current)
        diff_pixels = sum(
            sum(1 for pixel in diff.getdata() if any(c > 0 for c in pixel))
        )
        total_pixels = baseline.size[0] * baseline.size[1]
        similarity = 1 - (diff_pixels / total_pixels)
        
        # Save diff image if needed
        if diff_path and diff_pixels > 0:
            diff.save(diff_path)
        
        return similarity >= threshold, similarity

    3. Baseline Management System

    python

    import json
    from datetime import datetime
    
    class BaselineManager:
        def __init__(self, baseline_dir="baselines"):
            self.baseline_dir = baseline_dir
            os.makedirs(baseline_dir, exist_ok=True)
            
        def save_baseline(self, name, image_path):
            """Save a new baseline with metadata"""
            timestamp = datetime.now().isoformat()
            baseline_path = f"{self.baseline_dir}/{name}.png"
            metadata = {
                "created": timestamp,
                "source": image_path
            }
            
            # Save image
            Image.open(image_path).save(baseline_path)
            
            # Save metadata
            with open(f"{baseline_dir}/{name}.json", 'w') as f:
                json.dump(metadata, f)
                
            return baseline_path

    Advanced Features

    1. Region-Specific Comparison

    python


    def compare_regions(baseline_path, current_path, regions, threshold=0.95):
        """
        Compare specific regions of images
        regions: List of (x, y, width, height) tuples
        """
        baseline = Image.open(baseline_path)
        current = Image.open(current_path)
        results = []
        
        for region in regions:
            x, y, w, h = region
            baseline_crop = baseline.crop((x, y, x+w, y+h))
            current_crop = current.crop((x, y, x+w, y+h))
            
            is_similar, score = compare_images(
                baseline_crop, current_crop, threshold=threshold
            )
            results.append((region, is_similar, score))
        
        return results

    2. Dynamic Content Masking

    python


    def mask_dynamic_regions(image_path, regions, output_path=None):
        """
        Mask dynamic content regions with black rectangles
        """
        img = Image.open(image_path)
        draw = ImageDraw.Draw(img)
        
        for region in regions:
            x, y, w, h = region
            draw.rectangle((x, y, x+w, y+h), fill='black')
        
        if output_path:
            img.save(output_path)
        return img

    Putting It All Together

    python


    def run_visual_test(url, test_name, threshold=0.95):
        """Complete visual test workflow"""
        # Setup
        bm = BaselineManager()
        current_path = capture_screenshot(url, f"current_{test_name}.png")
        
        # Check if baseline exists
        baseline_path = f"baselines/{test_name}.png"
        if not os.path.exists(baseline_path):
            print(f"Creating new baseline for {test_name}")
            bm.save_baseline(test_name, current_path)
            return True
        
        # Compare images
        diff_path = f"diffs/diff_{test_name}.png"
        is_similar, score = compare_images(
            baseline_path, current_path, diff_path, threshold
        )
        
        # Generate report
        report = {
            "test_name": test_name,
            "passed": is_similar,
            "similarity_score": score,
            "diff_image": diff_path if not is_similar else None,
            "timestamp": datetime.now().isoformat()
        }
        
        return report

    Handling Common Challenges

    1. Cross-Browser Variations

      • Create separate baselines per browser

      • Adjust similarity thresholds per browser

    2. Responsive Testing

      • Test at multiple viewport sizes

      • Use device emulation in Selenium

    3. Test Maintenance

      • Implement baseline versioning

      • Add approval workflow for new baselines

    4. Performance Optimization

      • Cache screenshots

      • Parallelize tests

    Integration with Test Frameworks

    python


    import unittest
    
    class VisualTestCase(unittest.TestCase):
        @classmethod
        def setUpClass(cls):
            cls.bm = BaselineManager()
            
        def test_homepage_layout(self):
            report = run_visual_test(
                "https://example.com", 
                "homepage_desktop",
                threshold=0.98
            )
            self.assertTrue(report['passed'], 
                f"Visual regression detected. Similarity: {report['similarity_score']}")

    Reporting and Analysis

    python


    def generate_html_report(test_reports):
        """Generate visual test HTML report"""
        html = """
        <html><head><title>Visual Test Report</title></head>
        <body><h1>Visual Test Results</h1>
        <table border="1">
        <tr>
            <th>Test</th>
            <th>Status</th>
            <th>Similarity</th>
            <th>Diff</th>
        </tr>
        """
        
        for report in test_reports:
            status = "PASS" if report['passed'] else "FAIL"
            color = "green" if report['passed'] else "red"
            diff_link = f'<a href="{report["diff_image"]}">View</a>' if report["diff_image"] else "None"
            
            html += f"""
            <tr>
                <td>{report['test_name']}</td>
                <td style="color:{color}">{status}</td>
                <td>{report['similarity_score']:.2%}</td>
                <td>{diff_link}</td>
            </tr>
            """
        
        html += "</table></body></html>"
        return html

    Scaling Your Solution

    1. Parallel Execution

      • Use Selenium Grid

      • Implement multiprocessing

    2. Baseline Management

      • Store baselines in cloud storage

      • Add metadata tagging

    3. CI/CD Integration

      • Add as a test step in your pipeline

      • Configure failure thresholds

    Limitations to Consider

    • Maintenance overhead for baseline updates

    • Browser-specific rendering differences

    • Performance impact of image processing

    • Limited to pixel comparison (no semantic understanding)

    Conclusion

    Building a custom visual testing tool gives you flexibility but requires careful implementation. Start small with critical pages, then expand as needed. Consider these enhancements:

    1. Add machine learning for smarter diff detection

    2. Implement cloud storage for baselines

    3. Create a dashboard for trend analysis

    4. Add support for component-level testing

    Remember that commercial tools might become more cost-effective as your needs grow, but a custom solution can be perfect for specific requirements.

    Source: Read More

    Facebook Twitter Reddit Email Copy Link
    Previous ArticleHow to Perform Visual Testing Using Selenium: A Detailed Guide
    Next Article Advanced 15 Selenium Interview Questions with Coding Examples

    Related Posts

    Development

    Smashing Security podcast #423: Operation Endgame, deepfakes, and dead slugs

    June 25, 2025
    Machine Learning

    How to Evaluate Jailbreak Methods: A Case Study with the StrongREJECT Benchmark

    June 25, 2025
    Leave A Reply Cancel Reply

    For security, use of Google's reCAPTCHA service is required which is subject to the Google Privacy Policy and Terms of Use.

    Continue Reading

    CVE-2024-8988 – PeepSo Core: File Uploads Plugin WordPress Insecure Direct Object Reference

    Common Vulnerabilities and Exposures (CVEs)

    CVE-2025-6267 – Zhilink ADP Application Developer Platform SQL Injection Vulnerability

    Common Vulnerabilities and Exposures (CVEs)

    How to use Lotties in Figma

    Web Development

    Improve you C++ skills by coding an audio plugin

    Development

    Highlights

    WebStatus.dev: Now with more data, deeper insights, and a clearer path to Baseline

    May 29, 2025

    Announcing improvements to the Web Platform Dashboard. Source: web.dev: Blog 

    FedRAMP at Startup Speed: Lessons Learned

    June 18, 2025

    FBI investigating apparent ISIS attacks on Western websites

    April 9, 2025

    The Oblivion Remaster is already discounted but it’s so popular it’s crashing deal websites

    April 22, 2025
    © DevStackTips 2025. All rights reserved.
    • Contact
    • Privacy Policy

    Type above and press Enter to search. Press Esc to cancel.