Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular Unit Test Mock Replay Subject

I have a service with a replay subject.

export class UserService {
  public userChanged: ReplaySubject<User> = new ReplaySubject<User>();

...
public getUser(userId?): void {
    ...
    this.http.get(url, httpOptions).pipe(
      catchError(this.handleError('getUser', null, 'Couldn\'t get user', options))
    ).subscribe( (user: User) => {

       this.userChanged.next(user);

    });
  }

My component subscribes to userChanged.

this.userService.userChanged.subscribe((user) => {
  this.user = user;
});

Now, I want to mock my UserService in the component test:

1 option Testing Observables in Angular)

import { of } from 'rxjs';
...
    const userServiceSpy = jasmine.createSpyObj('UserService', {'userChanged': of({_id: '1'}) });

or 2 option)

    const userServiceSpy = jasmine.createSpyObj('UserService', {'userChanged': () => of({_id: '1'}) });

or 3 option angular test tutorial)

const userServiceSpy = jasmine.createSpyObj('UserService', ['userChanged']});
const userChangedSpy = userServiceSpy.userChanged.and.returnValue( of({_id: '1'})  );

+

TestBed.configureTestingModule({
  ...
  providers: [
    ...
    {provide: UserService, useValue: userServiceSpy}
  ],
  schemas: [NO_ERRORS_SCHEMA]
})

give me this err:

this.userService.userChanged.subscribe is not a function

Shouldn't of return an Observable to subscribe to?

Question: How to mock this?

like image 268
Andi Giga Avatar asked Dec 24 '22 00:12

Andi Giga


2 Answers

createSpyObj is used to to create spies on methods. You could use it for getUser method of UserService.

userChanged is just a property of the class. You don't need a spy for it.

What you can do is simply create a mock object that returns subject:

const userChanged = new Subject();

 providers: [
    ...
    {provide: UserService, useValue: { userChanged }}
  ],

{ userChanged } is equal to { userChanged: userChanged }

Then, in your beforeEach block you would emit a new user instance:

//...
beforeEach(() => {
   const myUser = new User(...)
   userChanged.next(myUser)
})

I recommend to do this in the beforeEach block to avoid side effects between different specs.

 providers: [
    ...
    {provide: UserService, useValue: { userChanged: of({id: 1}) }}
  ],

Another way of doing the same would be simply creating observable using of method same way you're doing it in your example.


If you really want to spy on subscribe method, you can create spy on it:

spyOn(userChanged, 'subscribe')


If you want to mix spyObject with properties, you can use spread operator:

const spyObj = {
  ... jasmine.createSpyObj('MyObject', ['spyMethod']),
  myProperty: true,
};

spyObj.spyMethod();

expect(spyObj.spyMethod).toHaveBeenCalled();
expect(spyObj.myProperty).toBeTrue();
like image 125
DDRamone Avatar answered Jan 06 '23 14:01

DDRamone


This might be a bit late.. But I was able to resolve this issue by following the below approach:

1. mockUserService = TestBed.get(UserService)
2. By calling the subscribe method using :
mockUserService.userChanged.Subscribe(data => {
// do something
})

Note: Code at line 1 & 2 must sit anywhere after TestBed.configureTestingModule declaration.

like image 44
Karthik Bhat Avatar answered Jan 06 '23 14:01

Karthik Bhat