Gargoyle
Gargoyle

Reputation: 10345

Verify observable value from BehaviorSubject in subscription

I've got a simple service that queries the web and then based on the return, it emits an event through a BehaviorSubject

export class ProducerService {
    static readonly baseUrl = `${environment.apiUri}/producers`
    private readonly admin = new BehaviorSubject<boolean>(false)
    readonly admin$ = this.admin.asObservable()

    constructor(private readonly http: HttpClient) {
    }

    queryAdmin(): void {
        this.http.get<boolean>(`${ProducerService.baseUrl}/admin`)
            .subscribe(x => this.admin.next(x))
    }
}

Now I'm trying to write a test that verifies if true is passed in that the admin$ variable gets set to true. I tried it like this

it('should emit true when an admin', async(() => {
    service.admin$.subscribe(x => expect(x).toBeTrue())

    service.queryAdmin()
    const req = httpMock.expectOne({url: `${ProducerService.baseUrl}/admin`, method: 'GET'})
    req.flush(true)
}))

That fails though saying "Expected false to be true". What am I doing wrong?

Upvotes: 0

Views: 1351

Answers (2)

Gargoyle
Gargoyle

Reputation: 10345

Had to do multiple things here. Couldn't use async or it didn't like the done method. Had to do the filter like @AliF50 suggested, and I had to pass in a value of 1 instead of true. So I ended up with this for the test:

it('should emit true when an admin', (done) => {
    service.admin$
        .pipe(filter(x => x))
        .subscribe(x => {
            expect(x).toBeTrue()
            done()
        })

    service.queryAdmin()
    const req = httpMock.expectOne({url: `${ProducerService.baseUrl}/admin`, method: 'GET'})
    req.flush(1)
})

That also meant I had to modify my queryAdmin method so that I did the !! like so:

queryAdmin(): void {
    // This is being done for the producer.service.spec.ts file because it
    // won't decode a 'true' value automatically, so I have to pass in a 1
    // as the body (i.e. a true value) and then this !!x converts that to true.
    // noinspection PointlessBooleanExpressionJS
    this.http.get<boolean>(`${ProducerService.baseUrl}/admin`).subscribe(x => this.admin.next(!!x))
}

Upvotes: 0

AliF50
AliF50

Reputation: 18869

The BehaviorSubject is "hot" so it is ready to go when you subscribe to it and it has an initial value of false, then you're asserting false toBeTrue.

Try to filter out the false values using the filter operator of Rxjs.

import { filter } from 'rxjs/operators';
....
it('should emit true when an admin', async((done) => {
    service.admin$.pipe(
      filter(admin => !!admin), // the value of admin has to be true for it to go into the subscribe block
    ).subscribe(x => { 
      expect(x).toBeTrue(); 
      done(); // add the done function as an argument and call it to ensure
    });       // test execution made it into this subscribe and thus the assertion was made
              // Calling done, tells Jasmine we are done with our test.

    service.queryAdmin()
    const req = httpMock.expectOne({url: `${ProducerService.baseUrl}/admin`, method: 'GET'})
    req.flush(true)
}))

Upvotes: 1

Related Questions