# 10 Angular Unit Tests to Prevent Memory Leaks You Didn't Know You Needed

---

Master Angular component cleanup with real-world unit tests for `setTimeout`, `Observables`, DOM listeners, `AsyncPipe`, and more. A complete guide to writing leak-free Angular apps.

Perfect — here are **10 comprehensive unit test cases** in Angular to ensure **memory leaks are avoided**. These cover all critical and edge async scenarios, such as:

* `setTimeout`
    
* `setInterval`
    
* `RxJS subscriptions`
    
* `AsyncPipe`
    
* `EventListeners`
    
* `Subjects`
    
* `AnimationFrames`
    
* `WebSocket/Custom cleanup`
    
* `ResizeObserver`
    
* `MutationObserver`
    

---

### ✅ 1. Unsubscribe from RxJS with `takeUntil`

```typescript
it('should call destroy$.next() and complete() on ngOnDestroy', () => {
  const nextSpy = jest.spyOn((component as any).destroy$, 'next');
  const completeSpy = jest.spyOn((component as any).destroy$, 'complete');

component.ngOnDestroy();
  expect(nextSpy).toHaveBeenCalledTimes(1);
  expect(completeSpy).toHaveBeenCalledTimes(1);
});
```

---

### ✅ 2. Clear `setInterval` on Destroy

```typescript
it('should clear interval on destroy', () => {
  const clearSpy = jest.spyOn(window, 'clearInterval');
  component.intervalId = setInterval(() => {}, 1000);

component.ngOnDestroy();
  expect(clearSpy).toHaveBeenCalledWith(component.intervalId);
  clearInterval(component.intervalId); // cleanup
});
```

---

### ✅ 3. Clear `setTimeout` on Destroy

```typescript
it('should clear timeout on destroy', () => {
  const clearSpy = jest.spyOn(window, 'clearTimeout');
  component.timeoutId = setTimeout(() => {}, 1000);

component.ngOnDestroy();
  expect(clearSpy).toHaveBeenCalledWith(component.timeoutId);
  clearTimeout(component.timeoutId); // cleanup
});
```

---

### ✅ 4. Remove Event Listener on Destroy

```typescript
it('should remove event listener on destroy', () => {
  const removeSpy = jest.spyOn(document, 'removeEventListener');
  component.listener = jest.fn();

document.addEventListener('click', component.listener);
  component.ngOnDestroy();
  expect(removeSpy).toHaveBeenCalledWith('click', component.listener);
});
```

---

### ✅ 5. Verify `AsyncPipe` Usage — No Manual Cleanup Required

```typescript
it('should have data$ defined for async pipe usage', () => {
  expect(component.data$).toBeDefined();
});
```

> *Note: AsyncPipe automatically unsubscribes on destroy.*

---

### ✅ 6. Unsubscribe from Manual Subject

```typescript
it('should unsubscribe from subject', () => {
  const sub = new Subject<void>();
  const spy = jest.fn();
  sub.subscribe(spy);

sub.next();
  sub.complete();
  expect(spy).toHaveBeenCalled();
});
```

---

### ✅ 7. Cancel `requestAnimationFrame` on Destroy

```typescript
it('should cancel requestAnimationFrame on destroy', () => {
  const cancelSpy = jest.spyOn(window, 'cancelAnimationFrame');
  component.rafId = requestAnimationFrame(() => {});

component.ngOnDestroy();
  expect(cancelSpy).toHaveBeenCalledWith(component.rafId);
});
```

---

### ✅ 8. Close Custom WebSocket or Observer on Destroy

```typescript
it('should close websocket connection on destroy', () => {
  component.socket = { close: jest.fn() } as any;
  component.ngOnDestroy();

expect(component.socket.close).toHaveBeenCalled();
});
```

---

### ✅ 9. Disconnect `ResizeObserver` on Destroy

```typescript
it('should disconnect ResizeObserver on destroy', () => {
  component.resizeObserver = { disconnect: jest.fn() } as any;
  component.ngOnDestroy();

expect(component.resizeObserver.disconnect).toHaveBeenCalled();
});
```

---

### ✅ 10. Disconnect `MutationObserver` on Destroy

```typescript
it('should disconnect MutationObserver on destroy', () => {
  component.mutationObserver = { disconnect: jest.fn() } as any;
  component.ngOnDestroy();

expect(component.mutationObserver.disconnect).toHaveBeenCalled();
});
```

---

### 🎯 Your Turn, Devs!

👀 **Did this article spark new ideas or help solve a real problem?**

💬 I’d love to hear about it!

✅ Are you already using this technique in your Angular or frontend project?

🧠 Got questions, doubts, or your own twist on the approach?

**Drop them in the comments below — let’s learn together!**

---

### 🙌 Let’s Grow Together!

If this article added value to your dev journey:

🔁 **Share it with your team, tech friends, or community** — you never know who might need it right now.

📌 Save it for later and revisit as a quick reference.

---

### 🚀 Follow Me for More Angular & Frontend Goodness:

I regularly share hands-on tutorials, clean code tips, scalable frontend architecture, and real-world problem-solving guides.

* 💼 [**LinkedIn**](https://www.linkedin.com/in/errajatmalik/) — Let’s connect professionally
    
* 🎥 [**Threads**](https://www.threads.net/@er.rajatmalik) — Short-form frontend insights
    
* 🐦 [**X (Twitter)**](https://twitter.com/er_rajatmalik) — Developer banter + code snippets
    
* 👥 [**BlueSky**](http://devrajat.bsky.social/) — Stay up to date on frontend trends
    
* 🌟 [**GitHub Projects**](https://github.com/malikrajat) — Explore code in action
    
* 🌐 [**Website**](https://malikrajat.github.io/) — Everything in one place
    
* 📚 [**Medium Blog**](https://medium.com/@codewithrajat/) — Long-form content and deep-dives
    
* 💬 [**Dev Blog**](https://dev.to/codewithrajat) — Free Long-form content and deep-dives
    
* ✉️ [**Substack**](https://codewithrajat.substack.com/) — Weekly frontend stories & curated resources
    
* 🧩 [**Portfolio**](https://rajatmalik.dev/) — Projects, talks, and recognitions
    
* ✍️ [**Hashnode**](https://hashnode.com/@codeswithrajat) — Developer blog posts & tech discussions
    

---

### 🎉 If you found this article valuable:

* Leave a **👏 Clap**
    
* Drop a **💬 Comment**
    
* Hit **🔔 Follow** for more weekly frontend insights
    

Let’s build cleaner, faster, and smarter web apps — **together**.

**Stay tuned for more Angular tips, patterns, and performance tricks!** 🧪🧠

[✨ Share Your Thoughts To 📣 Set Your Notification Preference](https://forms.gle/mdfXEVh3AxYwAsKw5)
