Creating and test components in Angular

Table of contents

No heading

No headings in the article.

creating a component

ng g c components/posts --skip-tests

This line will not generate the test file. We will make it later. This component will try to get the posts and delete the posts. Code of the postComponent is given below:

export class PostsComponent implements OnInit {
  posts: Post[] = [];
    constructor() { }

  ngOnInit(): void {
    this.getposts();
  }
  getPosts(){

  }
  deletePost(post:Post){}

}

We also need to create a model interface called Post and that is also given below:

export interface Post{
    id:number;
    title:string;
    body:string;
}

Source of data/routes: Click Here Now, let us create the post service for the PostComponent and here also we will skip creating the test file.

ng g s services/Post/Post --skip-tests

Then, add the following code to the Service class:

export class PostService {

  constructor(private http:HttpClient) { }
  getPosts(){
    return this.http.get<Post[]>('https://jsonplaceholder.typicode.com/posts')

  }
  deletePost(post:Post){
    return this.http.delete('https://jsonplaceholder.typicode/post/${post.id}')
  }
}

Let us complete the PostComponent

export class PostsComponent implements OnInit {
  posts: Post[] = [];
    constructor(private postService: PostService) { }

  ngOnInit(): void {
   this.getPosts();
  }
 getPosts() {
  this.postService.getPosts().subscribe((posts)=>{
    this.posts=posts;
  })
 }
 delete(post:Post){
  this.posts=this.posts.filter((p)=>p.id!=post.id);
  this.postService.deletePosts(post).subscribe();
 }
}

Here, we see that this component is dependant on the PostService. Also, our PostService is dependant on HttpClientService. Just keep this in mind somewhere. and, now let us start writing test code for the component.

import { Post } from "src/app/models/Post";
import { PostsComponent } from "./posts.component";

describe('Posts Component', () => {
    let POSTS:Post[];
    let component:PostsComponent;
    beforeEach(() => {
        POSTS=[
            {
                "id": 1,
                "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
                "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
              },
              {
                "id": 2,
                "title": "excepturi optio reprehenderit",
                "body": "expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
              },
              {
                "id": 3,
                "title": "provident occaecati excepturi optio reprehenderit",
                "body": "precusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
              },

        ];
        component=new PostsComponent();
    })

})

You will get error while creating an instance of PostComponent and this would happen because the we need PostService dependency and further it needs HttpClient dependency. Also, we want o write isolated test cases which means we don't want the test case to depend on any other Service. As we have done before in one of the previous articles, we have to create a mock of the service.

mockPostService=jasmine.createSpyObj(['getPosts','deletePost'])

Let us try writing the test case for the delete method now:

import { Post } from "src/app/models/Post";
import { PostsComponent } from "./posts.component";

describe('Posts Component', () => {
    let POSTS:Post[];
    let component:PostsComponent;
    let mockPostService:any;
    beforeEach(() => {
        POSTS=[
            {
                "id": 1,
                "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
                "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
              },
              {
                "id": 2,
                "title": "excepturi optio reprehenderit",
                "body": "expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
              },
              {
                "id": 3,
                "title": "provident occaecati excepturi optio reprehenderit",
                "body": "precusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
              },

        ];
        mockPostService=jasmine.createSpyObj(['getPosts','deletePost'])
        component=new PostsComponent(mockPostService);
    });
    describe('delete',() => {
        it('should delete the selected Post from the posts',() => {
            component.posts=POSTS;
            let post:Post;
            post=POSTS[0];
            component.delete(post);
            expect(component.posts.length).toBe(2);

        })
    })
})

Try running and checking the output,

image.png You will see that we are getting error as it is not able read Service methods. This was destined to happen because mockPostService is just a dummy of the PostService. And, hence it throws a variable. But, we knw that we can return a value for the mockServices which we create. so, let us add that in the code and then try running the test code. So, add the following line.

    describe('delete',() => {
        it('should delete the selected Post from the posts',() => {
            mockPostService.deletePost.and.returnValue(of(true));
            component.posts=POSTS;
            component.delete(POSTS[1]);
            expect(component.posts.length).toBe(2);

        })
    })

So, here we define the return value as true. So, basically we don't care what do we get as return value. Thus, we put anything and let us now try running and seeing the test result.

image.png

Completing the test case for delete method Now, let us add some more test cases in the spec file.


describe('Posts Component', () => {
    let POSTS:Post[];
    let component:PostsComponent;
    let mockPostService:any;
    beforeEach(() => {
        POSTS=[
            {
                "id": 1,
                "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
                "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
              },
              {
                "id": 2,
                "title": "excepturi optio reprehenderit",
                "body": "expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
              },
              {
                "id": 3,
                "title": "provident occaecati excepturi optio reprehenderit",
                "body": "precusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
              },

        ];
        mockPostService=jasmine.createSpyObj(['getPosts','deletePost'])
        component=new PostsComponent(mockPostService);
    });
    describe('delete',() => {
        it('should delete the selected Post from the posts',() => {
            mockPostService.deletePost.and.returnValue(of(true));
            component.posts=POSTS;
            component.delete(POSTS[1]);
            expect(component.posts.length).toBe(2);
        });
        it('should call the delete method in Post Service only once', () => {
            mockPostService.deletePost.and.returnValue(of(true));
            component.posts=POSTS;
            component.delete(POSTS[1]);
            expect(mockPostService.deletePost).toHaveBeenCalledTimes(1);
        });
        it('should delete the actual selected post in Posts', () =>{
            mockPostService.deletePost.and.returnValue(of(true));
            component.posts=POSTS;
            component.delete(POSTS[1]);
            for(let post of component.posts){
                expect(post).not.toEqual(POSTS[1]);
            }
        })
    })
})

Code till now: Click Here