Back to Appsmith

Appsmith Testing Quick Reference

.cursor/docs/references/testing-reference.md

1.996.1 KB
Original Source

Appsmith Testing Quick Reference

Testing Requirements

For Bug Fixes

  1. Unit Tests (Required)

    • Reproduce the bug scenario
    • Verify the fix works correctly
    • Test edge cases and potential regressions
    • Place in the same directory as the fixed code with .test.ts extension
  2. End-to-End Tests (Required for user-facing changes)

    • Create a Cypress test that simulates the user action that would trigger the bug
    • Verify the fix works correctly in the application context
    • Place in app/client/cypress/e2e/Regression/
  3. Redux/React Safety Tests (For Redux/React code)

    • Test with both valid and null/undefined state structures
    • Verify that property access is handled safely
    • Test edge cases where nested properties might not exist

For Feature Development

  1. Unit Tests (Required)

    • Test each component or function individually
    • Cover the main functionality, edge cases, and error conditions
    • Place alongside the implemented code
  2. Integration Tests (For complex features)

    • Test interactions between components
    • Verify that data flows correctly between components
  3. End-to-End Tests (For user-facing features)

    • Simulate user interactions with the feature
    • Verify that the feature works correctly in the application context

Test File Locations

  • Unit Tests: Same directory as the code being tested (e.g., Component.test.tsx)
  • Cypress E2E Tests: app/client/cypress/e2e/Regression/[Category]/[Feature]_spec.js
  • Backend Tests: app/server/src/test/java/com/appsmith/server/...

Templates

Unit Test for Bug Fix (React Component)

typescript
import React from "react";
import { render, screen, fireEvent } from "@testing-library/react";
import MyComponent from "./MyComponent";

describe("MyComponent bug fix", () => {
  it("should reproduce the bug scenario", () => {
    // Arrange: Setup the conditions that trigger the bug
    render(<MyComponent prop="value" />);
    
    // Act: Perform the action that triggers the bug
    fireEvent.click(screen.getByText("Button"));
    
    // Assert: Verify the bug is fixed
    expect(screen.getByText("Expected Result")).toBeInTheDocument();
  });
  
  it("should maintain existing functionality", () => {
    // Test that related functionality still works
    render(<MyComponent prop="otherValue" />);
    expect(screen.getByText("Other Result")).toBeInTheDocument();
  });
});

E2E Test for Bug Fix

javascript
describe("Feature Bug Fix", { tags: ["@tag.Bugfix", "@tag.Regression"] }, function() {
  before(() => {
    cy.login();
    cy.createTestWorkspace();
  });

  it("should no longer exhibit the bug", () => {
    // Steps to reproduce the bug
    cy.get("[data-cy=element]").click();
    cy.get("[data-cy=other-element]").type("value");
    
    // Verify the bug is fixed
    cy.get("[data-cy=result]").should("have.text", "Expected Result");
  });
});

Redux Safety Test

typescript
import { configureStore } from '@reduxjs/toolkit';
import reducer, { selectUserData } from './userSlice';
import { renderHook } from '@testing-library/react-hooks';
import { Provider } from 'react-redux';
import React from 'react';
import { useSelector } from 'react-redux';

describe("Redux safety tests", () => {
  // Test with missing nested properties
  it("should handle missing nested properties safely", () => {
    // Create store with incomplete state
    const store = configureStore({
      reducer: {
        user: reducer
      },
      preloadedState: {
        user: {
          // Missing nested user data structure
        }
      }
    });
    
    // Test selector with missing data
    const wrapper = ({ children }) => (
      <Provider store={store}>{children}</Provider>
    );
    
    const { result } = renderHook(() => useSelector(selectUserData), { wrapper });
    
    // Should not throw an error and return default/fallback value
    expect(result.current).toEqual(/* expected default value */);
  });
  
  it("should handle deep property access safely", () => {
    // Similar setup but with different state permutations
    // Test various incomplete state structures
  });
});

Unit Test for Redux Sagas

typescript
import { runSaga } from 'redux-saga';
import { mySaga } from './mySaga';

describe("mySaga", () => {
  it("should dispatch expected actions", async () => {
    // Mock dependencies
    const dispatched = [];
    const mockStore = {
      dispatch: (action) => dispatched.push(action),
      getState: () => ({ data: 'mock data' }),
    };
    
    // Run the saga
    await runSaga(mockStore, mySaga, { payload: { id: '123' } }).toPromise();
    
    // Assert expected actions were dispatched
    expect(dispatched).toEqual([
      { type: 'SOME_ACTION', payload: 'mock data' },
    ]);
  });
});

Best Practices

  1. Test the User Experience

    • Focus on testing what the user sees and experiences
    • Don't test implementation details unless necessary
  2. Use Descriptive Test Names

    • Tests should clearly describe what they're testing
    • Use format: should [expected behavior] when [condition]
  3. Isolate Tests

    • Each test should be independent
    • Don't rely on state from other tests
  4. Test Edge Cases

    • Empty input, invalid input, boundary conditions
    • Error states and recovery
    • Null/undefined in Redux state trees
    • Missing nested properties
  5. Keep Tests Fast

    • Tests should run quickly to encourage frequent testing
    • Use mocks for slow dependencies
  6. Test Coverage Guidelines

    • 80%+ coverage for critical paths
    • Focus on business logic rather than UI details
  7. Redux/React Safety Testing

    • Test selectors with incomplete state structures
    • Verify error boundaries catch property access errors
    • Test with various state permutations to ensure robustness

Running Tests

Frontend Unit Tests

bash
cd app/client
yarn test         # Run all tests
yarn test:watch   # Run in watch mode
yarn test:coverage # Generate coverage report

Cypress E2E Tests

bash
cd app/client
yarn cypress:open  # Open Cypress UI
yarn cypress:run   # Run headless

Backend Tests

bash
cd app/server
./mvnw test