Unit 6 - Notes
INT222
Unit 6: Testing and Deployment
Topic 1: Testing REST API
Testing REST APIs ensures that the backend services function correctly, handle errors gracefully, and maintain data integrity. In advanced web development, this usually involves automated testing frameworks integrated into the development workflow.
1.1 Types of API Testing
- Unit Testing: Testing individual functions or methods in isolation (e.g., testing a utility function that calculates a total price).
- Integration Testing: Testing how different modules work together. For APIs, this is the most critical phase, as it tests the HTTP request/response cycle, database interaction, and middleware.
- End-to-End (E2E) Testing: Simulating a complete user scenario from the frontend through the API to the database and back.
- Performance/Load Testing: Checking how the API behaves under heavy traffic (Tools: Apache JMeter, k6).
1.2 Key Tools and Frameworks (Node.js Ecosystem)
- Jest: A comprehensive testing framework by Facebook. It supports assertions, mocking, and code coverage.
- Mocha: A flexible test runner often paired with assertion libraries.
- Chai: An assertion library used with Mocha (allows BDD styles like
expectorshould). - Supertest: An HTTP assertion library specifically designed for testing Node.js HTTP servers. It simulates requests to your API without needing the server to be listening on a network port.
1.3 Anatomy of an API Test
When testing a REST API, assertions generally focus on three areas:
- HTTP Status Code: Did the server return 200 (OK), 201 (Created), 400 (Bad Request), etc.?
- Response Headers: Is the content-type
application/json? - Response Body (Payload): Does the JSON data match the expected structure and values?
1.4 Implementation Example (Jest + Supertest)
Scenario: Testing a GET /api/products endpoint.
Step 1: Setup
npm install --save-dev jest supertest
Step 2: The Test File (products.test.js)
const request = require('supertest');
const app = require('../app'); // Import your Express app
const mongoose = require('mongoose');
// Setup and Teardown for Database connection
beforeAll(async () => {
await mongoose.connect(process.env.TEST_DB_URI);
});
afterAll(async () => {
await mongoose.disconnect();
});
describe('GET /api/products', () => {
it('should return 200 OK and a list of products', async () => {
const response = await request(app).get('/api/products');
// Assert Status Code
expect(response.statusCode).toBe(200);
// Assert Headers
expect(response.headers['content-type']).toEqual(expect.stringContaining('json'));
// Assert Body Structure
expect(Array.isArray(response.body)).toBeTruthy();
expect(response.body.length).toBeGreaterThan(0);
// Assert Specific Data Fields
expect(response.body[0]).toHaveProperty('name');
expect(response.body[0]).toHaveProperty('price');
});
it('should return 404 if route does not exist', async () => {
const response = await request(app).get('/api/non-existent-route');
expect(response.statusCode).toBe(404);
});
});
1.5 Mocking vs. Live Testing
- Live Testing: Connects to a real test database. Provides the highest confidence but is slower and requires cleanup (database flushing) between tests.
- Mocking: Replaces the database or external services with "fake" functions.
- Pros: Fast, deterministic, no network required.
- Cons: Does not test the actual database query logic.
Topic 2: Deployment with GitHub
Modern deployment relies heavily on Git-based workflows (GitOps). Deployment is no longer manually uploading files via FTP; it is triggered by pushing code to a repository.
2.1 Continuous Integration/Continuous Deployment (CI/CD)
- CI (Continuous Integration): The practice of automating the integration of code changes from multiple contributors into a single software project. It involves automated building and testing.
- CD (Continuous Deployment/Delivery): Automatically releasing the code to the production environment after passing CI tests.
2.2 GitHub Actions
GitHub Actions is a CI/CD platform that allows you to automate your build, test, and deployment pipeline directly within GitHub.
Key Concepts:
- Workflow: A configurable automated process defined by a YAML file in
.github/workflows. - Event: A specific activity that triggers a workflow (e.g.,
push,pull_request). - Runner: A server (hosted by GitHub or self-hosted) that runs your workflow.
- Job: A set of steps that execute on the same runner.
2.3 Deployment Strategies using GitHub
A. Static Site Deployment (GitHub Pages)
Best for frontend-only React/Vue/Angular apps or HTML/CSS sites.
- Go to Repository Settings > Pages.
- Select the branch (usually
mainorgh-pages) and folder (/or/docs). - GitHub automatically builds and hosts the site at
username.github.io/repo-name.
B. PaaS Deployment (Heroku/Render/Railway)
Connecting a GitHub repo to a Platform as a Service (PaaS) allows for automatic deployment of full-stack (Node.js, Python, etc.) applications.
- Auto-Deploy: The PaaS detects a push to the
mainbranch. - Build Command: The platform runs
npm installandnpm build. - Start Command: The platform runs
npm start.
C. Custom Workflow Example (Deploying Node.js to a Server)
This YAML file (.github/workflows/deploy.yml) demonstrates a pipeline that tests the code and then deploys it.
name: Node.js CI/CD
on:
push:
branches: [ "main" ]
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: '18.x'
- name: Install dependencies
run: npm ci
- name: Run Tests
run: npm test
deploy:
needs: build-and-test # Only runs if tests pass
runs-on: ubuntu-latest
steps:
- name: Deploy to Production via SSH
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.SSH_KEY }}
script: |
cd /var/www/myapp
git pull origin main
npm install
pm2 restart app
2.4 Managing Secrets
Never commit .env files to GitHub. Instead, use GitHub Secrets:
- Go to Settings > Secrets and variables > Actions.
- Add
DB_URI,API_KEY, etc. - Access them in workflows using
${{ secrets.SECRET_NAME }}.
Topic 3: Third-Party Rendering
Third-party rendering refers to the architectural pattern where parts of the application's rendering process or content generation are offloaded to external services, Headless CMSs, or specialized rendering engines, rather than being handled entirely by the primary application server or the browser.
3.1 The Rendering Spectrum
To understand third-party rendering, one must understand the standard models:
- CSR (Client-Side Rendering): Browser downloads empty HTML + JS. JS fetches data and builds the UI (e.g., standard React).
- SSR (Server-Side Rendering): Server builds HTML per request (e.g., Next.js).
3.2 Headless CMS (Content Rendering)
In this model, the "Third Party" manages the content and provides an API. The rendering logic exists in your app, but the content is rendered from a third-party source.
- Examples: Contentful, Strapi, Sanity.io.
- Workflow:
- Marketers update content in Contentful (Third Party).
- The application (via API) fetches this JSON content during the build process (SSG) or request time (SSR).
- The application renders the JSON into HTML components.
3.3 Serverless and Edge Rendering
Using third-party cloud providers (AWS Lambda, Cloudflare Workers, Vercel Edge Functions) to render pages closer to the user.
- Edge Rendering: Instead of one central server rendering the page, a third-party network (CDN) executes the rendering logic at a node physically closest to the user.
- Benefit: Drastically reduced latency and Time To First Byte (TTFB).
3.4 Embedded Third-Party Rendering
Sometimes, specific UI components are rendered entirely by a third party and embedded into the application.
- iFrames: The classic method. A window into another site. Secure isolation but poor performance and SEO.
- Use case: Youtube embeds, Google Maps, Payment Gateways (Stripe Elements).
- Web Components / Micro-frontends:
- Different teams or third-party vendors own specific parts of a page (e.g., the Header is rendered by Team A's service, the Product List by Team B's service).
- These fragments are composed together at runtime.
3.5 Third-Party Rendering Services (BaaS)
Backend-as-a-Service platforms often handle the rendering of authentication UIs or database views.
- Auth0 / Firebase Auth: When a user logs in, they are often redirected to a page rendered entirely by Auth0, then redirected back with a token. The application offloads the complex rendering of login forms, 2FA, and password resets to the third party.
3.6 Pros and Cons of Third-Party Rendering
| Feature | Advantage | Disadvantage |
|---|---|---|
| Development Speed | Faster; no need to build CMS or Auth UIs from scratch. | Reliance on external documentation. |
| Maintenance | Third party handles security updates and scaling. | Vendor lock-in; migration is difficult. |
| Performance | CDNs/Edge rendering is faster than centralized servers. | External API latency can block rendering if not handled asynchronously. |
| Reliability | SLAs usually guarantee high uptime. | If the third party goes down, that part of your app breaks. |