Being dogmatic about the Clean Architecture in React is unprofitable

April 20th, 2023

Sometimes it will bring more benefits if we deviate from the original concept.

For example, instead of inverting the API adapter dependency, we could tightly couple it to the implementation (direct import) and mock the API request in our tests.

In the integration test:

  • When using the React Testing Library, mock with mswjs.io
  • With Cypress Components Testing, mock using cy.intercept()
  • In Playwright Components Testing, mock with page.route()

The bonus idea is to reference mocked API endpoint via an intermediate variable (or enum) both in the production and test code:

PaymentApiService.ts
export const PAYMENT_API = 'https://api.domain.com/payment';
 
export async function createPayment(payload: Payment): Payment {
  const response = await fetch(PAYMENT_API, {
    method: 'POST',
    body: JSON.stringify(payload),
    });
 
    ...
CreateInvalidInternationalPayment.test.tsx
import { PAYMENT_API } from '../PaymentApiService';
 
describe('Create invalid international payment', () => {
  it('see feedback underneath input fields with invalid input', () => {
    cy.intercept('POST', PAYMENT_API, {
      statusCode: 400,
    });
 
    cy.mount(<PaymentPage />);
    ...

Is this integration test still brittle?
It is, but tradeoffs are minor.

Here are the benefits:

  • No dependency injection overhead
  • The test resilient to API endpoint url changes
  • Test documenting APIs behavior (which, how mocked)
  • Tight coupling — the natural thing to Frontend engineers ;)

I learned this lesson from Paul Hammond and Robert Smith.

Now it's up to you.

Blindly follow the Clean Architecture and invert every dependency. Or learn from the LinkedIn developers community and deviate for simplicity.


Discuss on LinkedIn


Need help? Send me an email to .

Copyright © 2012 - 2023 Nik Sumeiko. All rights reserved.