Lesson 17

Testing Integration with a Server

Using E2E tests to test against the real server

PRO

Lesson Outline

Testing Integration with a Server and Using Mock Backend

With the server set up, we will now be able to start implementing the functionality for this requirement:

  • A user should only be able to log in to the application with a valid license key from SendOwl

As always, we will start with the E2E test and work from there. As you will soon see, this E2E test is going to take us down a rather long road.

Create the E2E Test

In order to implement this functionality, we are clearly going to need to implement some sort of login page that will be displayed before the rest of the application. Since we will be adding a new page, we are going to create a new spec file for our E2E tests as well as a new navigation command and helper functions.

Modify cypress/support/commands.ts to include the following function:

const navigateToLoginPage = () => {
  cy.visit('/');
};

IMPORTANT: Remember to add the Cypress command at the bottom of the file, and add the new function to the Cypress namespace as well.

Add the following helper functions to cypress/support/utils.ts:

export const getKeyInput = () => cy.get('[data-test="key-input"] input');

export const getLoginButton = () => cy.get('[data-test="login-button"]');

Now we will be able to grab a reference to a key-input field that will be used to enter the key, and a login button that will be used to trigger sending the key to the server.

If you've been paying especially close attention, you may notice that the navigateToLoginPage function is the same as what we are currently using for the home page. The login page will eventually become the new default page, which means the navigateToHomePage function for the home page will no longer work and it's going to break all of our tests - we'll deal with that later.

Now let's create the E2E test for the login page.

Create a file at cypress/e2e/login.cy.ts and add the following:

import {
  getKeyInput,
  getLoginButton,
  getModuleListItems,
} from '../support/utils';

describe('Login', () => {
  beforeEach(() => {
    cy.navigateToLoginPage();
  });

  it('a user should be able to reach the home page by providing a valid license key', () => {
    getKeyInput().type('abcd-egfh-ijkl-mnop');
    getLoginButton().click();

    getModuleListItems().first().should('contain.text', 'Module One');
  });
});

We've created a test that will first send a valid key to the input field (remember, with the server we set up any value will be treated as a valid key) and then the login button will be clicked. This is the first time we have seen type which allows us to send text input to an element, but as are most things with Cypress it is quite intuitive to understand.

Let's run that test now and see what kind of errors we get.

Run the E2E tests

You should get the following result:

Expected to find element: [data-test="key-input"] input, but never found it.

We haven't implemented our login page yet so our <input> field for entering the key doesn't exist. Let's work on that now.

Run the following command to generate the LoginPage:

ionic g page Login

Modify the routes in src/app/app-routing.module.ts to reflect the following:

import { NgModule } from '@angular/core';
import { PreloadAllModules, RouterModule, Routes } from '@angular/router';

const routes: Routes = [
  {
    path: 'home',
    loadChildren: () =>
      import('./home/home.module').then((m) => m.HomePageModule),
  },
  {
    path: '',
    redirectTo: 'login',
    pathMatch: 'full',
  },
  {
    path: 'module/:id',
    loadChildren: () =>
      import('./lesson-select/lesson-select.module').then(
        (m) => m.LessonSelectPageModule
      ),
  },
  {
    path: 'module/:moduleId/lesson/:lessonId',
    loadChildren: () =>
      import('./lesson/lesson.module').then((m) => m.LessonPageModule),
  },
  {
    path: 'login',
    loadChildren: () =>
      import('./login/login.module').then((m) => m.LoginPageModule),
  },
];

@NgModule({
  imports: [
    RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules }),
  ],
  exports: [RouterModule],
})
export class AppRoutingModule {}

Notice that we have changed the redirectTo to login instead of home since we want the login page to be our default page now.

Modify src/app/login/login.page.spec.ts to reflect the following:

import {
  ComponentFixture,
  TestBed,
  waitForAsync,
  fakeAsync,
  tick,
} from '@angular/core/testing';
import { IonicModule, NavController } from '@ionic/angular';

import { LoginPage } from './login.page';

describe('LoginPage', () => {
  let component: LoginPage;
  let fixture: ComponentFixture<LoginPage>;

  beforeEach(waitForAsync(() => {
    TestBed.configureTestingModule({
      declarations: [LoginPage],
      providers: [
        {
          provide: NavController,
          useValue: {
            navigateRoot: jest.fn(),
          },
        },
      ],
      imports: [IonicModule.forRoot()],
    }).compileComponents();

    fixture = TestBed.createComponent(LoginPage);
    component = fixture.componentInstance;
    fixture.detectChanges();
  }));

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});

Now if we run our E2E tests again with npm run e2e. We should get the following if we run all the tests...

Failing E2E tests

Isn't it kind of fun to watch everything fail catastrophically? Our tests are failing completely now. Since we are stuck on the login page (due to our recent routing change) all of our existing tests fail. Although this example is somewhat in your face, this is what tests are for. We can change code and develop new features with extra confidence that if something goes wrong our tests will (hopefully) fail and let us know about it. If we did somehow manage to replace the default route with a blank page that is impossible to navigate away from without realising it, we have our tests to back us up.

We need to start implementing the unit tests for the Login page so that we can create the functionality necessary to actually log in. We will also be creating a separate service to handle the requests to the server. Once we've done that, we can start fixing up all of our tests - don't worry, we'll be seeing green again in no time.

PRO

Thanks for checking out the preview of this lesson!

You do not have the appropriate membership to view the full lesson. If you would like full access to this module you can view membership options (or log in if you are already have an appropriate membership).