Lesson 31 — Testing Forms & API Calls (Real-World Scenarios)

🧭 Introduction

Forms and API calls are where most real-world bugs happen:

  • Invalid inputs slip through
  • APIs fail silently
  • Loading states are forgotten
  • Error messages don’t appear

Testing these correctly separates beginner tests from professional-grade tests.

In this lesson, you’ll learn how to test user forms and API interactions the way production React apps do — confidently and reliably.


🎯 What You’ll Learn in This Lesson

By the end of this lesson, you will understand:

  • How to test form inputs and submission
  • How to simulate user typing and clicking
  • How to test validation errors
  • How to mock API calls
  • How to test loading and error states
  • Common mistakes to avoid

🧠 Testing Forms: Core Principles

When testing forms, always remember:

Test what the user does and sees — not the form library.

Focus on:
✔ Typing
✔ Submitting
✔ Seeing validation
✔ Seeing success or failure


🧩 Example Form Component

function LoginForm({ onSubmit }) {
  const [email, setEmail] = React.useState("");
  const [error, setError] = React.useState("");

  const handleSubmit = e => {
    e.preventDefault();
    if (!email) {
      setError("Email is required");
      return;
    }
    onSubmit(email);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        placeholder="Email"
        value={email}
        onChange={e => setEmail(e.target.value)}
      />
      <button type="submit">Login</button>
      {error && <p>{error}</p>}
    </form>
  );
}


🧪 Testing User Typing (Very Important)

fireEvent.change(
  screen.getByPlaceholderText("Email"),
  { target: { value: "user@test.com" } }
);

This simulates real typing.


🧪 Testing Form Submission

fireEvent.click(screen.getByText("Login"));


🧠 Testing Validation Errors

fireEvent.click(screen.getByText("Login"));
expect(
  screen.getByText("Email is required")
).toBeInTheDocument();

✔ Tests validation
✔ Tests user feedback


🧠 Testing Successful Submission

const mockSubmit = jest.fn();

render(<LoginForm onSubmit={mockSubmit} />);

fireEvent.change(
  screen.getByPlaceholderText("Email"),
  { target: { value: "user@test.com" } }
);

fireEvent.click(screen.getByText("Login"));

expect(mockSubmit).toHaveBeenCalledWith("user@test.com");

✔ Behavior-focused
✔ No internal state testing


🧠 Testing API Calls (Big Concept)

Real apps talk to APIs.

But in tests:
❌ Don’t hit real servers
❌ Don’t depend on network

Instead:

Mock API responses


🧩 Example API Component

function Users() {
  const [users, setUsers] = React.useState([]);
  const [error, setError] = React.useState("");

  useEffect(() => {
    fetch("/api/users")
      .then(res => res.json())
      .then(setUsers)
      .catch(() => setError("Failed to load users"));
  }, []);

  if (error) return <p>{error}</p>;
  return (
    <ul>
      {users.map(u => (
        <li key={u.id}>{u.name}</li>
      ))}
    </ul>
  );
}


🧪 Mocking API Success Response

global.fetch = jest.fn(() =>
  Promise.resolve({
    json: () => Promise.resolve([
      { id: 1, name: "Avni" }
    ])
  })
);


🧪 Testing API Success UI

expect(await screen.findByText("Avni"))
  .toBeInTheDocument();

Notice:

  • findBy waits for async UI updates

🧠 Testing Loading State (Important)

If your component has loading UI:

if (loading) return <p>Loading...</p>;

Test it:

expect(screen.getByText("Loading..."))
  .toBeInTheDocument();


🧪 Mocking API Failure

global.fetch = jest.fn(() =>
  Promise.reject(new Error("API Error"))
);


🧠 Testing Error UI

expect(
  await screen.findByText("Failed to load users")
).toBeInTheDocument();


🧠 Async Testing Rules (Must Remember)

✔ Use findBy for async UI
✔ Avoid arbitrary setTimeout
✔ Let the test wait for UI updates

Bad:

setTimeout(() => {}, 1000);

Good:

await screen.findByText("Data loaded");


🚨 Common Mistakes in Form & API Testing

❌ Testing form libraries internals

❌ Making real API calls

❌ Ignoring error states

❌ Not testing loading UI

❌ Over-mocking everything


🎯 Best Practices (Senior-Level)

✅ Test full user flow
✅ Mock APIs centrally
✅ Test success + failure
✅ Keep tests readable
✅ Avoid flaky async logic
✅ Use semantic queries


❓ FAQs — Forms & API Testing

🔹 Should I test backend validation?

No — frontend behavior only.


🔹 Should I mock fetch or axios?

Yes — always.


🔹 Are API tests slow?

No — if mocked properly.


🔹 Is this asked in interviews?

Yes — very frequently.


🧠 Quick Recap

✔ Test typing & submission
✔ Validate error messages
✔ Mock API success & failure
✔ Test loading & error UI
✔ Focus on user behavior


🎉 Conclusion

Forms and APIs are where:

  • Users interact
  • Bugs hide
  • Apps break

Testing them properly ensures your app:

  • Handles real-world scenarios
  • Fails gracefully
  • Delivers consistent UX

With this lesson, you’ve completed SECTION 9 — Testing React Applications 🧪⚛️


👉 Next Section

SECTION 10 — TypeScript with React

👉 Next Lesson

Lesson 32 — TypeScript Fundamentals for React

Leave a Comment