While this Loan App system dates back to 2018—built on Laravel 5.x and a React 16 (without Hooks) front-end, it had a structured, layered test suite. In 2018, the popular choices included ESLint and JSCS for linting, PHPUnit for PHP, Jest + Enzyme for React, and Cypress or Selenium for end-to-end. Below, we’ll map out five layers of testing with tools available at the time.
- Static Analysis (ESLint, JSCS, PHP_CodeSniffer, PHPMD)
- Unit Tests (PHPUnit, Jest + Enzyme)
- Integration Tests (Laravel HTTP Tests, nock or fetch-mock)
- Component Tests (Jest + Enzyme, React Test Utilities)
- End-to-End Tests (Cypress or Selenium WebDriver)
1. Static Analysis: Linting & Style Checking
Tools: ESLint (with ES6/React plugins), JSCS, Prettier (early adoption), PHP_CodeSniffer, PHPMD
Why?
- Enforce consistent style and catch syntax errors before runtime
- Identify code smells in PHP (unused vars, complexity) via PHPMD
- Automate formatting with Prettier or JSCS
Front‑end Example (.eslintrc.js
):
module.exports = {
parser: 'babel-eslint', // support ES6+ syntax
extends: ['eslint:recommended', 'plugin:react/recommended'],
plugins: ['react'],
env: { browser: true, jest: true, es6: true },
rules: { 'react/prop-types': 'warn', 'no-console': 'off' },
};
Back‑end Example (.phpcs.xml
):
<ruleset name="LoanApp">
<rule ref="PSR2"/>
<rule ref="PHP_CodeSniffer\Standards\Generic\Sniffs\Namespaces\NamespaceDeclarationSniff"/>
</ruleset>
Run these tools locally and integrate into CI to block bad commits.
2. Unit Tests: Isolate Core Logic
Tools: PHPUnit 6.x (Laravel built‑in), Jest 22.x, Enzyme 3.x
PHPUnit Example (InterestCalculator):
// app/Services/InterestCalculator.php
public function calculate($principal, $rate, $days) {
return round($principal * $rate/365 * $days, 2);
}
// tests/Unit/InterestCalculatorTest.php
class InterestCalculatorTest extends TestCase {
public function testDailyInterest(): void {
$calc = new \App\Services\InterestCalculator();
$this->assertEquals(8.22, $calc->calculate(10000, 0.30, 10));
}
}
Jest + Enzyme Example (DocumentTypeUtil):
// src/utils/documentType.js
export function isValidPdf(file) {
return file.type === 'application/pdf';
}
// __tests__/documentType.test.js
import { isValidPdf } from '../src/utils/documentType';
test('accepts PDF files', () => {
const file = { type: 'application/pdf' };
expect(isValidPdf(file)).toBe(true);
});
test('rejects non‑PDF files', () => {
const file = { type: 'image/png' };
expect(isValidPdf(file)).toBe(false);
});
3. Integration Tests: API & Data Layer
Tools: Laravel HTTP Tests (built‑in), nock (for Node), fetch‑mock or jest‑fetch‑mock
Laravel Example (LoanRequestController
):
// tests/Feature/LoanRequestTest.php
public function testCreateLoanRequest(): void {
$user = factory(User::class)->create();
$this->actingAs($user)
->postJson('/api/loans', [
'institution_id' => 1,
'net_worth' => 50000,
'collaterals' => ['Real Estate'],
])
->assertStatus(201)
->assertJson(['status' => 'pending']);
$this->assertDatabaseHas('loan_requests', ['user_id' => $user->id]);
}
React Integration (fetch-mock):
// __tests__/upload.integration.js
import fetchMock from 'fetch-mock';
import { uploadDocument } from '../src/api';
describe('uploadDocument', () => {
afterEach(() => fetchMock.restore());
it('returns URL on success', async () => {
fetchMock.post('/api/uploads', { url: '/docs/1.pdf' });
const response = await uploadDocument(new Blob());
expect(response.url).toBe('/docs/1.pdf');
});
});
4. Component Tests: React UI Assertions
Tools: Jest + Enzyme (shallow, mount)
Example (LoanStatusCard):
import React from 'react';
import { shallow } from 'enzyme';
import LoanStatusCard from '../src/components/LoanStatusCard';
describe('<LoanStatusCard />', () => {
it('shows pending status', () => {
const wrapper = shallow(<LoanStatusCard status="pending"/>);
expect(wrapper.text()).toContain('pending');
});
});
Mock contexts or Redux stores as needed to supply props and state.
5. End‑to‑End Tests: Full User Journeys
Tools: Cypress 3.x (released mid‑2018), Selenium WebDriver with Mocha or PHPUnit
Cypress Example:
// cypress/integration/loan.spec.js
describe('Loan Application Flow', () => {
it('SME can submit a loan request', () => {
cy.visit('/login');
cy.get('#username').type('owner1');
cy.get('#password').type('password');
cy.contains('Sign In').click();
cy.contains('New Loan Request').click();
cy.get('#institution').select('Bank A');
cy.get('#netWorth').type('75000');
cy.get('#collateral').attachFile('asset.png');
cy.contains('Submit').click();
cy.contains('Status: pending').should('be.visible');
});
});
Run E2E tests on CI or locally—Cypress offers a fast, reliable runner in your browser.
Conclusion
A layered approach, linting, unit, integration, component, and E2E, ensures that each part of Laravel + React application remained reliable as we continued to enhance and maintain it.
Leave a Reply