Test Driven Development in an Angular World
Tags: testing, tdd, angular, typescript
Published: 2019-04-26 09:12:16

## **Wait! What is TDD?**
So, you keep hearing about this TDD thing and want to start using it but have no idea why. Don’t worry, the internet to the rescue! But first, what is TDD?

**TDD** stands for Test Driven Development and is a way of writing code. It means that you first write your test, you see it fail and then you write the code that makes it pass. This is also called the *red*-*green* approach. Long story short, it’s Development Driven by Tests.

**Small example**: Let’s say you want to write a method that adds 2 numbers. Now, instead of implementing the method, you write the test first:

```typescript
it('should add the values', () => {
  const result = addValues(2, 4);
  
  expect(result).toBe(6);
});

let addValues = (value1, value2) {}
```

Now, you run the test and you see it fail:

![Test fails](https://thepracticaldev.s3.amazonaws.com/i/wlmnqu8web7mqbrwl7cr.png)

Only now, you know what the problem is, so you can implement the method:

```typescript
it('should add the values', () => {
  const result = addValues(2, 4);
  
  expect(result).toBe(6);
});

let addValues = (value1, value2) {
  return value1 + value2;
}
```

And now, the test passes:
![Test passes](https://thepracticaldev.s3.amazonaws.com/i/fg2dlngj94q3qrhut4y4.png)

Now, that’s quite satisfying, right?

So, this does a few things:

 * Makes sure your code is tested, so you don’t have to spend the last 2 days of the sprint writing tests for all the code that you wrote( which never happens )
 * Makes sure you write code in a pragmatic way. There is a thing called YAGNI(You aren’t going to need it) which is about developers over-thinking and trying to solve climate change and world hunger with the next bit of code. Safe to say, most often than not, this makes the code overcomplicated without any real benefit.
 * When you finished writing a feature this way, you saw all the tests pass, that gives you confidence that if/when you refactor if the tests are still passing, then you didn’t break anything. How often were you afraid of breaking the product with your simple Pull Request? Feels something like this:

![Bus reporter](https://media.giphy.com/media/JocjV2cE8Uet2/giphy.gif)

* Last but not least, it’s all about the colors! Seeing colors makes us happy. Seeing something failing and then passing, makes us even happier.

3 months and 500 tests later, we have a real sense of confidence over the product that we are building.

The keyword here is: **CONFIDENCE**

## **Now, back to Angular and our component**

So, now you have an idea about what **TDD** is. Next stop, using it in our component. For this post, I am assuming you have a basic understanding of **Angular**, so I won’t go into details on how it works.

I’ll be using **Jest** for my tests but the principles are the same.

For our example, let use a fictitious app that lets us create lists with movies. Now, somewhere in space and time, we decide that we also want a page where a user can see his/her favorite movies. So, without further ado, let’s build it!

First stop, let’s generate the component: 
```
ng g c favorite-movies
```

Now, this will generate a component. So, let’s see, what do we want to do first? We want to add a title like *Favorite movies*. Let’s do it!

**WAAAAIT!** What did we discuss? Write tests, then write the code that fixes it.

So, what would that look like? Something like this:

```typescript
  describe('Render', () => {
    beforeEach(() => {
      fixture.detectChanges();
    });

    it('should have a title', () => {
      const titleElements = fixture.debugElement.queryAll(By.css('h1'));
      expect(titleElements.length).toBe(1);
      expect(titleElements[0].nativeElement.innerHTML).toBe('Favorite movies');
    });
  });
```

We created a new describe block for **Rendering** and there we added a new test that checks that there is an **h1** tag and that it has the correct content. 
Now, we run the test and, big surprise, it fails! Don’t fret, we can fix it:
```html
<h1>Favorite movies</h1>
```

We added this to our HTML template and now our test passes!

**Congratulations!** You wrote your first bit of code in a TDD way. Take some time to pat yourself on the back. Good job! Now stop it. We have more work to do.

Next, we want to test that, given a list of movies, they are shown in the HTML. So, what do we do? We write a test for it!

```typescript

const favoriteMoviesToUse: Movie[] = [
  { title: 'Interstellar' } as Movie,
  { title: 'The big Lebowski' } as Movie,
  { title: 'Fences' } as Movie
];

describe('FavoriteMoviesComponent', () => {
  beforeEach(() => {
    fixture = TestBed.createComponent(FavoriteMoviesComponent);
    component = fixture.componentInstance;
    component.favoriteMovies = favoriteMoviesToUse;
  });
  
  describe('Render', () => {
    
    it('show all the favorite movies', () => {
      const movieElements = fixture.debugElement.queryAll(By.css('.movie'));
      expect(movieElements.length).toBe(favoriteMoviesToUse.length);
    });
    
     it('should show the movie titles', () => {
      const movieElements = fixture.debugElement.queryAll(By.css('.movie'));
      movieElements.forEach((movieElement: DebugElement, index) => {
         expect(movieElement.nativeElement.innerHTML).toContain(favoriteMoviesToUse[index].title);
      });
    });
  });
});
```

Here we added a new **@Input()** property to the component, we created a new list of movies and we passed those as input to the component. Then, we are testing that the rendered HTML contains the correct amount of elements with the class movie and that all the **movie** titles are displayed.

Of course, this test fails. So, let’s make it pass:

```html
<div class="movie" *ngFor="let movie of favoriteMovies">
  {{ movie.title }}
</div>
```

This is we add to our template. Yei! Tests pass now!

**Neeeeext!**

What if our data actually comes from a service asynchronously?

Let’s adjust our test for that:

```typescript
describe('FavoriteMoviesComponent', () => {
  let component: FavoriteMoviesComponent;
  let fixture: ComponentFixture<FavoriteMoviesComponent>;
  let favoriteMovieService: FavoriteMoviesService;

  beforeEach(() => {
    fixture = TestBed.createComponent(FavoriteMoviesComponent);
    component = fixture.componentInstance;
    favoriteMovieService = TestBed.get(FavoriteMoviesService);
    jest.spyOn(favoriteMovieService, 'getFavoriteMovies').mockReturnValue(of(favoriteMoviesToUse));
  });

  describe('Getting the movies', () => {
    it('should get the movies from the service', () => {
      fixture.detectChanges();
      expect(favoriteMovieService.getFavoriteMovies).toHaveBeenCalled();
    });
  });
});
```

Now, our tests assume a service is injected. What we do is mock the response and check that the service is called. Tests fail, let’s fix them!

```typescript
export class FavoriteMoviesComponent implements OnInit {
  favoriteMovies$: Observable<Movie[]>;
  constructor(private favoriteMovieService: FavoriteMoviesService) {}

  ngOnInit() {
    this.favoriteMovies$ = this.favoriteMovieService.getFavoriteMovies();
  }
}
```

Now our component instead of having an **@Input()**, uses a service. The last puzzle piece is the template:

```html
<ng-container *ngIf="(favoriteMovies$ | async); let favoriteMovies">
  <div class="movie" *ngFor="let movie of favoriteMovies">
    {{ movie.title }}
  </div>
</ng-container>
```

So, now we have a fully working component that gets data from a service and renders it. What else do we need? Nothing! Time to go home and play some Red Dead Redemption 2 🐴.

**WAAAAAIT!** What if there are errors?

Ok, ok, let write a few tests to make sure that in case something fails, it is properly handled:

```typescript
    it('should show an error if getting the movies fail', () => {
      const errorToThrow = 'User not found';
      jest
        .spyOn(favoriteMovieService, 'getFavoriteMovies')
        .mockReturnValue(throwError(errorToThrow));

      fixture.detectChanges();

      const errorElement = fixture.debugElement.queryAll(By.css('.error'));
      expect(errorElement.length).toBe(1);
      expect(errorElement[0].nativeElement.innerHTML).toContain(errorToThrow);
    });

    it('should not show an error if getting the movies succeeds', () => {
      fixture.detectChanges();

      const errorElement = fixture.debugElement.queryAll(By.css('.error'));
      expect(errorElement.length).toBe(0);
    });
```

So, we want to make sure that the error is displayed if getting the favorite movies fails, and it is hidden if everything goes according to plan.

What we need is to catch the error in the component:

```typescript
  ngOnInit() {
    this.favoriteMovies$ = this.favoriteMovieService.getFavoriteMovies().pipe(
      catchError((error: any) => {
        this.error = error;

        return of([]);
      })
    );
  }
```

And to show the error in the template:

```html
<div class="error" *ngIf="error">
  {{ error }}
</div>
```

Now, after all our hard work we have a fully working component that uses an async service to get data, handles errors and renders everything that we expect it to.

Now, time for some eye candy:

![All tests pass](https://thepracticaldev.s3.amazonaws.com/i/oye2ejlhmofd43yvm337.png)

Pretty cool right?

Now, in order for this to work, you don’t need to implement the service. It just needs to exist and have a method that is called **getFavoriteMovies**.

## **Wrap-up**

I know, I know, lots of words. I tried to be as succinct as I could without omitting any important information. Easier said than done. This is my first Dev.to article, so, I’m still learning. Hopefully, in the next one, we’ll talk about testing an **Angular Service**. **TDD** of course.

Hope you have enjoyed this program 📺. See you in the next one!

https://twitter.com/_utukku