Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ngrx how to test a guard

Tags:

angular

ngrx

I want to test this simple guard both canActivate and canLoad How can manage it ? I did my first step manage the injected store

@Injectable({
  providedIn: 'root'
})
export class AuthGuard implements CanActivate, CanLoad {
    constructor(private store: Store<AuthState>) {}

    canActivate(): Observable<boolean> {
        return this.store.pipe(
            select(selectIsAuthenticated),
            map(isValidToken => {
                if (!isValidToken) {
                    this.store.dispatch(new Logout());
                    return false;
                }
                return true;
            }),
         take(1)
       );
    }

    canLoad(): Observable<boolean> {
        return this.store.pipe(
            select(selectIsAuthenticated),
            map(isValidToken => {
                if (!isValidToken) {
                    this.store.dispatch(new Logout());
                    return false;
                }
                return true;
            }),
            take(1)
        );
    }
}

My first step

export const authReducer: ActionReducerMap<{}> = {
  status: {}
};
describe('AuthGuard', () => {
  let store: Store<{}>;
  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [StoreModule.forRoot({}).forFeature('auth', authReducer)],
      providers: [Store, AuthGuard]
    });
    store = TestBed.get(Store);
  });

  it('should ...', inject([AuthGuard], (guard: AuthGuard) => {
    expect(guard).toBeTruthy();
  }));
});

But what about testing canActivate and canLoad ? I've to mock the select and how ?

like image 218
user3887366 Avatar asked Dec 26 '18 18:12

user3887366


People also ask

How do I test my NgRx store?

Testing NgRx facade using override/MockStore I recommend you use MockStore (with/without overrideSelector) to mocking the store state. If you want to involve the selectors in the test (integtration testing them), you should use MockStore : You set the state using the setState method from MockStore .

How do I test NgRx reducer?

Test a reducer Reducers are so simple to test as they are pure functions. We just need to call the reducer function and pass in a fake piece of state and an action and then check the new state slice returns.

How do you test canActivate?

First we mock authService with a stubbed return value of true . Then we create an instance of our MockRouter and an instance of AuthGuard using our two mocks. We then add our expectation that calling canActivate should return true . Now we'll add our second test case for when canActivate returns false .


1 Answers

Please check comments in the code. To test such classes you don't need TestBed.

describe('AuthGuard', () => {
    let guard: AuthGuard;
    let store: Subject<any> & any;

    // because it's a simple class and
    // we don't test templates, inputs, outputs etc,
    // we can use simple objects.
    beforeEach(() => {
        // mocked store can be a simple BehaviorSubject.
        store = new BehaviorSubject({});
        // and we need a spy of course
        store.dispatch = jasmine.createSpy('dispatch');
        // now we can create guard
        guard = new AuthGuard(store);
    });

    // don't forget to kill subscriptions.
    afterEach(() => {
        store.complete();
    });

    describe('canActivate', () => {
        it('logouts on an empty token', () => {
            // setting store state we want.
            store.next({
                authFeatureName: {
                    isLoggedIn: false,
                }
            });

            // toBeObservable comes from https://www.npmjs.com/package/jasmine-marbles
            // it's an awesome tool to test rxjs
            // we expect that canActivate will emit false and close the stream - take(1).
            expect(guard.canActivate()).toBeObservable(cold('a|', {
                a: false,
            }));

            // also we need to check that an action was dispatched.
            expect(store.dispatch).toHaveBeenCalledWith(jasmine.any(Logout));
        });

        it('returns true on valid token', () => {
            // setting our store.
            store.next({
                authFeatureName: {
                    isLoggedIn: true,
                }
            });

            // check that it emits true now
            expect(guard.canActivate()).toBeObservable(cold('a|', {
                a: true,
            }));

            // and that it doesn't dispatch any actions.
            expect(store.dispatch).not.toHaveBeenCalled();
        });
    });
});
like image 57
satanTime Avatar answered Oct 31 '22 18:10

satanTime