Search code examples
rxjsobservableangularfire

AngularFire testing for an object to exist using RxJS Subject


I am trying to test for an object to exist in my AngularFire table. I am having issues returning the subject to detect if the file exists or not.

/**
 * Check if the Id exists in storage
 * @param Id string | number Key value to check
 * @returns Subject<boolean>
 */
public Exists(Id:string):Subject<boolean> {
    const Status$:Subject<boolean> = new Subject<boolean>();

    let RecordExists:boolean = false;
    this.AfDb_.object<T>(`_Testing_/${Id}`).valueChanges()
        .subscribe( (OneRecord:T) => {
            if (OneRecord.Key_ !== undefined && OneRecord.Key_ !== null && OneRecord.Key_.length > 0) {
                RecordExists = true;
            }
        })
    ;
    Status$.next(RecordExists);
    return Status$;
}

This is always returning undefined. My automated tests then fail as well.

it('should confirm a record exists in storage', fakeAsync( () => {
    let Exists:boolean;
    const Status$:Subject<boolean> = ServiceUnderTest.Exists('Good');    // This exists in Firebase
    Status$.subscribe( (Result:boolean) => {
        Exists = Result;
    });
    flushMicrotasks();
    Status$.unsubscribe();
    expect(Exists).toBeTrue();
}));

I have access in Firebase to /Testing/Good which is an object with a structure of Key_ and Name.

Modules from package.json

"@angular/fire": "^5.4.2",
"firebase": "^7.9.3",

However, if I simply try to return a result without going directly to AngularFire, these tests work.

public Exists(Id:string):BehaviorSubject<boolean> {
    const Status:BehaviorSubject<boolean | undefined> = new BehaviorSubject<boolean | undefined>(undefined);

    Status.next(true);
    return Status;
}

Solution

  • You have to call next on subject when RecordExists came from .valueChanges() e.g.:

        let RecordExists:boolean = false;
        this.AfDb_.object<T>(`_Testing_/${Id}`).valueChanges()
            .subscribe( (OneRecord:T) => {
    
    
                if (OneRecord.Key_ !== undefined && OneRecord.Key_ !== null && OneRecord.Key_.length > 0) {
                    Status$.next(true);
                } else {
                     Status$.next(false);
               }
            })
        ;
        return Status$;
    

    Your code behave in different way in test and simple example because both are call this .valueChanges() in synchronous way so .next() is called after subscribe. In real life valueChanges is async so subscribe is called before next.

    ====================Edited=====================

    to connect with real database you have to modify your test to be async (because connection is async:

    it('should confirm a record exists in storage',((done) => {
        Status$.subscribe( (Result:boolean) => {
          expect(Exists).toBeTrue();
          done()
        });
    d}))