TestingQuality EngineeringLatest

Quality Engineering at Scale: Unit Tests, Cypress, Git Hooks & SonarCloud Integration

December 20, 2024 β€’ 15 min read

Testing Strategy Foundation

  • πŸ§ͺUnit Testing Excellence – Jest examples with real payment logic
  • πŸ“ŠTesting Pyramid – 70% unit, 20% integration, 10% E2E
  • πŸ”„Cypress Integration – Complete user journey testing
  • πŸ”—API Integration Tests – Backend/frontend communication

Automation & Quality Gates

  • ⚑Git Hooks Setup – Pre-commit and pre-push automation
  • πŸ”SonarCloud Integration – Continuous code quality inspection
  • πŸš€GitHub Actions – Automated quality gate workflows
  • πŸ“ˆQuality Metrics – Coverage, maintainability, reliability

Quality isn't just about writing testsβ€”it's about building a culture and infrastructure that prevents bugs from reaching production. After implementing comprehensive quality engineering practices across multiple production systems, I've learned that the best testing strategies combine automated testing, static analysis, and continuous integration into a seamless development workflow.

The Testing Pyramid in Practice

Unit Tests (70%)
β€’ Fast execution
β€’ Individual functions
β€’ High coverage
β€’ Immediate feedback
Integration Tests (20%)
β€’ API endpoints
β€’ Database interactions
β€’ Service communication
β€’ Real data flows
E2E Tests (10%)
β€’ User journeys
β€’ Browser automation
β€’ Critical paths
β€’ Production-like

The testing pyramid ensures maximum coverage with optimal execution speed and maintainability.

Unit Testing Excellence with Jest

Effective unit tests should be fast, isolated, and provide clear feedback. Here's how we structure unit tests for complex business logic:

// PaymentProcessor.test.js - Comprehensive unit testing example
import PaymentProcessor from '../PaymentProcessor';
import { mockPaymentGateway, mockAuditLogger } from '../__mocks__';

describe('PaymentProcessor', () => {
  let paymentProcessor;
  let mockGateway;
  let mockLogger;

  beforeEach(() => {
    mockGateway = mockPaymentGateway();
    mockLogger = mockAuditLogger();
    paymentProcessor = new PaymentProcessor(mockGateway, mockLogger);
  });

  describe('processPayment', () => {
    it('should successfully process a valid payment', async () => {
      // Arrange
      const paymentRequest = {
        amount: 99.99,
        currency: 'AUD',
        cardToken: 'tok_1234567890',
        customerId: 'cus_test123'
      };
      
      mockGateway.charge.mockResolvedValue({
        id: 'charge_test123',
        status: 'succeeded',
        amount: 9999
      });

      // Act
      const result = await paymentProcessor.processPayment(paymentRequest);

      // Assert
      expect(result.success).toBe(true);
      expect(result.chargeId).toBe('charge_test123');
      expect(mockLogger.logPaymentSuccess).toHaveBeenCalled();
    });

    it('should handle payment failures gracefully', async () => {
      mockGateway.charge.mockRejectedValue({
        type: 'card_error',
        code: 'card_declined'
      });

      const result = await paymentProcessor.processPayment({
        amount: 50.00,
        currency: 'AUD'
      });

      expect(result.success).toBe(false);
      expect(result.error.type).toBe('card_error');
    });
  });
});

Cypress E2E Testing Implementation

End-to-end tests validate complete user journeys. Here's how we implement robust E2E testing:

// cypress/e2e/checkout-flow.cy.js
describe('E-commerce Checkout Flow', () => {
  beforeEach(() => {
    cy.task('db:seed');
    cy.intercept('POST', '/api/payments/process', {
      fixture: 'payment-success.json'
    }).as('processPayment');
  });

  it('completes full purchase journey', () => {
    cy.visit('/products/wireless-headphones');
    cy.get('[data-testid="add-to-cart-btn"]').click();
    cy.get('[data-testid="checkout-btn"]').click();

    // Fill shipping information
    cy.get('input[name="email"]').type('test@example.com');
    cy.get('input[name="address"]').type('123 Test Street');
    
    // Fill payment information
    cy.get('input[name="cardNumber"]').type('4242424242424242');
    cy.get('input[name="expiryDate"]').type('12/25');
    
    cy.get('[data-testid="complete-purchase-btn"]').click();
    cy.wait('@processPayment');
    
    cy.url().should('include', '/order-confirmation');
    cy.get('[data-testid="order-confirmation"]')
      .should('contain', 'Order Confirmed');
  });
});

Git Hooks for Quality Gates

Automated quality checks prevent poor code from entering the repository:

# .husky/pre-commit
#!/usr/bin/env sh
echo "πŸ” Running pre-commit checks..."

# Lint staged files
npx lint-staged

# Type checking
npx tsc --noEmit

# Security audit
npm audit --audit-level moderate

echo "βœ… Pre-commit checks completed!"

# package.json scripts
{
  "scripts": {
    "test:unit": "jest --testPathPattern=__tests__/unit",
    "test:integration": "jest --testPathPattern=__tests__/integration",
    "test:e2e": "cypress run",
    "lint": "eslint . --ext .js,.jsx,.ts,.tsx --fix",
    "prepare": "husky install"
  }
}

# lint-staged.config.js
module.exports = {
  '*.{js,jsx,ts,tsx}': [
    'eslint --fix',
    'prettier --write',
    'jest --findRelatedTests --passWithNoTests'
  ]
};

SonarCloud Integration & Quality Metrics

# sonar-project.properties
sonar.projectKey=quality-engineering-example
sonar.organization=handy-hasan
sonar.sources=src
sonar.tests=src
sonar.test.inclusions=**/*.test.js,**/*.spec.js
sonar.javascript.lcov.reportPaths=coverage/lcov.info

# GitHub Actions Quality Pipeline
name: Quality Analysis
on: [push, pull_request]
jobs:
  quality:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'
      - name: Install dependencies
        run: npm ci
      - name: Run tests with coverage
        run: npm run test:coverage
      - name: SonarCloud Scan
        uses: SonarSource/sonarcloud-github-action@master
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}

Quality engineering transformed how we ship software at The Good Guys. Our comprehensive testing strategy reduced production bugs by 85% and increased developer confidence. The upfront investment in quality infrastructure pays dividends in faster delivery and fewer production issues.

Share: