Forem

Cover image for Unit/Component Testing React Apps: From 'Why Bother?' to 'Can't Live Without It'
Mohamed ikram
Mohamed ikram

Posted on

Unit/Component Testing React Apps: From 'Why Bother?' to 'Can't Live Without It'

🚨 Anatomy of a 3 AM Production Crisis: A Testing Tale

🎭 The Scene:

// The innocent-looking code that ruined everyone's night
const getDomainUrl = (config: BackendConfig) => {
  return config.domain; // What could go wrong? Everything.
};
Enter fullscreen mode Exit fullscreen mode

πŸ” Let's Debug Together: The Case of the Phantom Redirect

🚨 The Crime Scene

Dashboard users land at undefined/rest-path after a "harmless" config change

  • Suspect: A sneaky backend configuration tweak
  • Weapon: Missing validation for domain paths
  • Motive: "It worked in dev!" 🀦

Unit testing

describe('Domain Configuration', () => {
  it('should reject missing domain configuration', () => {
    const emptyConfig = {} as BackendConfig;
    expect(() => getDomainUrl(emptyConfig)).toThrow();
  });
});
Enter fullscreen mode Exit fullscreen mode

Code

const getDomainUrl = (config: BackendConfig): string => {
  if (!config.domain) {
    throw new DomainConfigError('Domain URL is required');
  }
  if (!isValidDomainUrl(config.domain)) {
    throw new DomainConfigError('Invalid domain URL format');
  }
  return config.domain;
};
Enter fullscreen mode Exit fullscreen mode

Component Testing

describe('RedirectHandler', () => {
  it('should handle domain configuration gracefully', () => {
    const mockConfig = {};
    const { getByTestId } = render(<RedirectHandler config={mockConfig} />);

    expect(getByTestId('config-error')).toBeInTheDocument();
    expect(captureError).toHaveBeenCalledWith(expect.any(DomainConfigError));
  });
});
Enter fullscreen mode Exit fullscreen mode

Code

const RedirectHandler: React.FC<{ config: BackendConfig }> = ({ config }) => {
  const [error, setError] = useState<string>();
  const navigate = useNavigate();

  useEffect(() => {
    try {
      const domainUrl = getDomainUrl(config);
      // Safely handle navigation
      navigate(`${domainUrl}/rest-path`);
    } catch (e) {
      if (e instanceof DomainConfigError) {
        setError(e.message);
        // Log to monitoring system
        captureError(e);
      }
    }
  }, [config, navigate]);

  if (error) {
    return (
      <ErrorBoundary>
        <ConfigurationError 
          message={error}
          retry={() => window.location.reload()}
        />
      </ErrorBoundary>
    );
  }

  return <LoadingSpinner />;
};
Enter fullscreen mode Exit fullscreen mode

πŸ› οΈ The Evolution of Robust Domain Configuration

🎯 What Changed?

  • Prevention Layers:

    • πŸ›‘οΈ Custom error types (InvalidDomainException)
    • πŸ”’ Domain validation before redirect logic
    • 🚨 Error boundaries to catch misconfigurations
  • Test-First Approach:

    • πŸ§ͺ Wrote failing test for missing config first
    • βœ… Added domain validation logic
    • πŸ’₯ Simulated edge cases (malformed URLs, empty configs)
  • Better Error UX:

    • πŸ“’ User-friendly message: "Oops! We hit a snag – try again?"
    • πŸ”„ Retry button with auto-logging for engineers
    • πŸ“Š Integrated error tracking (Sentry/Datadog)

πŸš€ The Results

  • βœ… No more undefined redirects
  • πŸ” Clear error tracking
  • 😴 Happy users (and developers who can sleep at night!)

🎯 The Tale of Two Developers: TDD vs. "We'll Test Later"

πŸŒ™ Bugs Bunny (Traditional):

  • πŸ”₯ 3 AM: Debugging while production burns
  • πŸ‡ Result: Chasing midnight bugs, endless firefighting

⭐ Iron Man (TDD):

"Test first, code with confidence."

  • πŸ›Œ Result: Deploys and sleeps soundly
  • πŸ›‘οΈ Secret Weapon: Tests act as code armor

πŸ”„ The Real Difference:

Same deadline, drastically different outcomes.

While Bugs Bunny chases production bugs at midnight, Iron Man’s approach means:

πŸ›‘οΈ Prevention > Cure

  • Catches bugs before they reach production
  • Each test is a shield against future issues
  • No more 3 AM emergency fixes

🎯 Design That Makes Sense

  • Tests force you to think before coding
  • Code becomes naturally modular
  • Refactoring becomes a breeze

πŸ“š Code That Tells a Story

  • Tests document how things should work
  • New team members get up to speed faster
  • Requirements are crystal clear

Top comments (0)