Test Driven Development with Protractor/Jasmine (Legacy)
This module is deprecated and no longer receives updates. Protractor is likely being removed as the default from Angular applications and Protractor itself will likely stop receiving updates and development in the future. I would recommend checking out the Test Driven Development with Cypress/Jest as a replacement.
The First Tests
WARNING: This module is deprecated and no longer receives updates. Protractor is likely being removed as the default from Angular applications and Protractor itself will likely stop receiving updates and development in the future. I would recommend checking out the Test Driven Development with Cypress/Jest as a replacement.
Creating the first tests for our application
DEPRECATEDModule Outline
- Resources PRO
- Lesson 1: Introduction PRO
- Lesson 2: Introduction to Test Driven Development PRO
- Lesson 3: Testing Concepts PRO
- Lesson 4: Jasmine, Karma, and Protractor PRO
- Lesson 5: A Simple Unit Test PRO
- Lesson 6: A Simple E2E Test PRO
- Lesson 7: Introduction to Angular's TestBed PRO
- Lesson 8: Setting up Tests PRO
- Lesson 9: Test Development Cycle PRO
- Lesson 10: Getting Ready PRO
- Lesson 11: The First Tests PRO
- Lesson 12: Injected Dependencies & Spying on Function Calls PRO
- Lesson 13: Building out Core Functionality PRO
- Lesson 14: Testing Asynchronous Code PRO
- Lesson 15: Creating a Mock Backend PRO
- Lesson 16: Setting up the Server PRO
- Lesson 17: Testing Integration with a Server PRO
- Lesson 18: Testing Storage and Reauthentication PRO
- Lesson 19: Refactoring with Confidence PRO
- Lesson 20: Conclusion PRO
Lesson Outline
The First Tests
Right now we just have a blank application that doesn't do much of anything. However, we've got everything that we need for tests configured and ready to go and we have a bit of background knowledge about testing, so let's get started.
In a previous lesson we discussed how to choose what to test first, and we ended up deciding on this requirement:
- A user should be able to view a list of modules for the course
Seeing this requirement, if we were not using Test Driven Development we might start working on the template for our home page, or maybe we would create a provider to handle loading in modules. However, writing code without a test is a big no-no in TDD, so we are instead going to start with a test.
With that in mind, we could still just go create a provider, set up a unit test for it, and start working on loading in some modules. But as we have discussed, unit tests alone are not sufficient to test an application as a whole. An E2E test is a good way to test that the various components in the application are working together as intended, and they are also a great way to enforce the requirements of the application. We will be sticking to the methodology of creating an E2E test first to drive out the unit tests.
Let's create our first E2E test now. Since this is the first E2E test we are creating we are going to go through it in a lot of detail. Throughout this process, let's keep the steps in mind from the Test Development Cycle lesson:
- Write an E2E test for some specific requirement
- Check that the E2E test fails
- Based on the failure you get from the E2E test, decide what functionality needs to be worked on in order to get it passing
- Write a unit test for the functionality you have decided that you need to create to get the E2E test to pass
- Check that the unit test fails
- Implement the code to satisfy the unit test
- Check if the E2E test passes now
- If the E2E test passes, go to Step 1. If the E2E test fails, go to Step 3.
Create an E2E Test
We are at Step 1 now, so the first thing we will need to do is create an E2E test.
Create a file at e2e/src/home.e2e-spec.ts and add the following:
import { HomePageObject } from './home.po';
describe('Home', () => {
let homePage: HomePageObject;
beforeEach(async () => {
homePage = new HomePageObject();
await homePage.navigateTo();
});
});
Before we even add a test, this is the basic structure for our spec file. We have a beforeEach
block that will be run before each test that we create in this file. This block should effectively "reset" our test to the state it needs to be in to be run independently from any previous tests.
We are creating a reference to something called HomePageObject. As we discussed in the Jasmine, Karma, and Protractor lesson, a page object allows us to create helper functions for our tests, which will make our tests more maintainable.
Inside of this beforeEach
block we set up a new reference to our HomePageObject
and we call the navigateTo
method that will make sure we are on the Home page before we begin our test. Since we haven't created the page object for the Home page yet, let's do it now.
Create a file at e2e/src/home.po.ts and add the following:
import { browser, by, element, ElementFinder } from 'protractor';
export class HomePageObject {
navigateTo() {
return browser.get('/');
}
getModuleListItems() {
return element.all(by.css('.module-list ion-item'));
}
}
We set up the navigateTo
method here which will just point the browser to the URL for the root page, and we also set up a function called getModuleListItems
. In a moment we will be writing a test that checks whether there is a list of modules available, so we need a way to grab the elements of that list.
Of course, that list doesn't exist yet (nor should it) so how do we properly set up a reference to it? You just guess! Grab a reference to the element in the way you want it to work. At this point in time, I think the modules will likely be displayed as a list. I'll add a class of module-list
to the <ion-list>
so I can grab a reference to all items in that list with the CSS selector .module-list ion-item
. It doesn't matter if this actually ends up being how it works, you could be completely wrong or maybe you change your mind and come back to change this later - it's just a place to start.
Generally, we will create a new E2E spec file and a corresponding page object for each page in the application that we start a test from. An E2E test will often involve visiting multiple pages, but if a test starts on the home page then that is where we will define the test.
Now that we have our page object set up, let's add the actual test to our spec file.
Modify e2e/src/home.e2e-spec.ts to reflect the following:
import { HomePageObject } from './home.po';
describe('Home', () => {
let homePage: HomePageObject;
beforeEach(async () => {
homePage = new HomePageObject();
await homePage.navigateTo();
});
it('should be able to view a list of modules', async () => {
const numberOfModulesInList = await homePage.getModuleListItems().count();
expect(numberOfModulesInList).toBe(5);
});
});
We've added a test now that says should be able to view a list of modules
. It's a pretty simple test, all we are doing is grabbing the reference to the items in the module list using the function we set up in a page object, and then we use the count
method to return the number of references it found. If there are the correct number of ion-items
found, then this count will be 5
. We use the toBe
matcher to check that this is actually the case. Using pseudo code, this would look like:
expect(the number of ion-items in the module list).toBe(5)
At this point, we've written the test in the way that we want the application to work. We are referencing things that don't exist yet, so this will cause errors. That's exactly what we want, though, the errors will tell us what we need to work on first.
With the test defined, it's always important to run it first. You might want to just jump straight into coding the feature because you know the test will obviously fail, but a couple of reasons to actually run the test first are:
- It might not fail. The test might have been written wrong and always pass, but if you never saw it fail you may incorrectly assume that you have successfully implemented your code because the test is passing
- It forces you to work within the parameters of the test. If you jump straight into the code you might start implementing code irrelevant to the test. If you run the test first and see specifically what is failing, you can focus on that first.
Run the following command to run the E2E tests:
npm run e2e
You might get a lot of errors appearing in the terminal, but what we are interested in is the Failures section that should look like this:
**************************************************
* Failures *
**************************************************
1) Home should be able to view a list of modules
- Expected 0 to be 5.
Executed 2 of 2 spec (1 FAILED) in 0.542 sec.
[20:56:17] I/launcher - 0 instance(s) of WebDriver still running
[20:56:17] I/launcher - chrome #01 failed 1 test(s)
[20:56:17] I/launcher - overall: 1 failed spec(s)
[20:56:17] E/launcher - Process exited with error code 1
We can see that the test we have just written fails, which is exactly what we want. However, the default test included in the app.e2e-spec.ts file is also being run and eventually that is going to cause us problems. You could just remove that test when you run into issues with it, or just delete the should be blank
test from app.e2e-spec.ts now. If you do delete it now, you should see that the E2E tests now only execute 1 test instead of 2:
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).