TDD in Angular - Understanding an Angular Unit Test
Tags: angular, javascript, testing, jasmine
Published: 2020-04-21 22:23:39

In my [previous post](https://dev.to/qarunqb/tdd-in-angular-the-basics-of-general-testing-2bfb), I talked about the Basics of General Testing. It was a short introduction into the different types of testing and how to approach testing in Jasmine. 

In this article, I want to take a look at the auto-generated unit test files of an Angular Application and explain what's happening. I'll unit test services for my examples since it's a good place to start understanding the fundamentals. Also, Angular Components have a bit more going on under the hood and that requires an article of its own.

Many Angular developers get confused and even overwhelmed by what's happening in a `.spec.ts` file. This article will change that.

_Code for this article can be found [here](https://github.com/Qarun-Qadir-Bissoondial/ng-jasmine-testing-demo/tree/understanding-an-angular-unit-test)._

### Angular Services

Services are by far the easiest to unit test. They're simply Injectable classes that may or may not have some state and contain a collection of functions. However, they shouldn't be taken lightly. **Your services are where all your business logic should be.** Therefore, testing them as much as you can will prove useful. 

Let's take a look at an InventoryService and its auto-generated `.spec.ts` file: 

{% gist https://gist.github.com/Qarun-Qadir-Bissoondial/ded5dd8013e0241f5e1ca2acb8e95ab3 %}

{% gist https://gist.github.com/Qarun-Qadir-Bissoondial/5102541495b429529e3a165320ea4903 %}

There's a few points to note here.

There's a `describe` block that groups all our tests together for this service. A variable called `service` is also initialized.

The `beforeEach` block contains code that is run before every single unit test in this spec file. It helps us have a clean slate before running each test so that previous tests don't interfere. This helps with the essence of unit testing (testing one thing in isolation without any external factors).
_There are other blocks of code like this to help us maintain clean tests, namely `beforeAll`, `afterEach` and `afterAll`. You can read more about them and more pretty cool testing tools in the [Jasmine Global API](https://jasmine.github.io/api/3.3/global)._

Looking inside the `beforeEach` block, we see 2 unfamiliar pieces of code. `TestBed.configureTestingModule` creates a dummy module for us to work with. `TestBed.inject` initializes our service and injects it into that dummy module. This holds true for components, pipes, guards, etc. This is the unit testing philosophy combined with Angular's architecture. 

An Angular application must have at least one module so a dummy module is created with only the piece of code being tested (in this case, the service) and nothing else. This way, nothing else from the outside can interfere with the tests. Pure Isolation. 

Each `it` block of code is a unit test. `it` is a function that accepts 2 arguments: A string describing the test and a function that must contain an `expect` assertion function. This `expect` function is what Jasmine runs to assert expected values against actual results.

`it('should be created'), () => {...}` is a unit test that is always created for any component, pipe, service, etc. It doesn't make sense looking at other unit tests if we can't initialize our code in the first place.

### Building our Service

Let's say that I wanted to add some code to track the number of items in my inventory and a way to increment and decrement the amount.

**Remember! We're approaching this from a TDD standpoint. We can write empty placeholders and then Tests first!**

{% gist https://gist.github.com/Qarun-Qadir-Bissoondial/fb8a3d6652fd92117985e83a6139c14e %}

We write tests for what we want our code to do and then consider other cases. Tests for main functionality should look something like this:

{% gist https://gist.github.com/Qarun-Qadir-Bissoondial/90a5509c08b954bf5346c7b1e49d30f2 %}

You'll notice I used `fdescribe` as opposed to `describe`. This means "Focused Describe" and Jasmine will only run this suite of tests instead of all tests in the Application. 

When we run our unit tests, we'll notice some failures (as expected). 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/4nrktbe93ebfqolauih8.png)

It's telling us that the "should increment the count" and "should decrement the count" tests are failing. This is expected since we didn't write any code in there as yet. Let's change that.

{% gist https://gist.github.com/Qarun-Qadir-Bissoondial/a34c248fb7c7c2cb5fd26269311f114c %}

And now our tests are passing: 

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/zwo6jatwc07nzmnn97n8.png)

The more astute among you might have realized that our decrement function isn't finished. We shouldn't be able to decrement if the `inventoryCount` is already 0.

A reasonable test for this can be:

```typescript
it('should not decrement when count is 0', () => {
    expect(service.inventoryCount).toBe(0);
    service.decrementCount();
    expect(service.inventoryCount).toBe(0);
  });
```

Re-running the tests gives us:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/hv3uqrex8wshlfpdhalj.png)

Our function currently decrements whatever value is stored in the service. We want it to decrement only when the value isn't 0. This is also a simple way to make sure that `inventoryCount` never falls below 0 (assuming you want your system like that).

We can modify the function to be:

```typescript
 decrementCount() {
    if (this.inventoryCount === 0) return;

    this.inventoryCount--;
  }
```

Now our tests are passing again.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/34i5n4qv8awfob2rinvs.png)

### Conclusion

In this article, we covered the following:

 - The basic structure of an Angular Unit Test
 - What happens in the `beforeEach` block
 - Why a dummy module is necessary for testing
 - Building an Angular Service using TDD

There are a lot more of these articles to come where we'll dive deeper into services, mocking, component life cycles, etc. Stay tuned for more and thanks a bunch for reading! :smile: