Build and test Angular apps using Docker
In this article, Karsten Sitterberg explains how you can create Angular CLI apps inside Docker containers.
Docker containers are a good approach to isolate, distribute and reproduce build environments consistently. Using Docker in conjunction with build servers like Jenkins or Gitlab CI is becoming increasingly popular because containers are much easier on the system resources. Especially if different versions of a software – in our example the Angular CLI – have to be supported simultaneously, containers can provide environments well separated against each other. Other than this, it is really easy to get going with Docker: To start a project with Docker and Angular, take one of to the following Docker images provided on Dockerhub.
- trion/ng-cli for simple Angular CLI usage without tests
- trion/ng-cli-karma supporting unit tests with Angular CLI and Karma using the Chromium browser, bases on trion/ng-cli
- trion/ng-cli-e2e supporting e2e tests in addition with Selenium/Webdriver, bases on trion/ng-cli-karma
If you have already installed Docker and run on a Linux system, you can start right away. Otherwise you will need to first install the Docker runtime. See the installation steps here.
For quick test run it is sufficient to let Angular CLI print the version number from the docker container. To prevent Docker from keeping the container around after finishing we will use ‘–rm’ as parameter. Angular CLI prints the version when called with the ‘-v’ parameter.
docker run --rm trion/ng-cli ng -v
Docker provides an isolation layer in the form of containers. Tasks and applications are therefore run inside a container. The environment inside the container has own users along with own ID, group, and directory schemes. When a file is created inside the container, its access rights should be mapped to the right user. For example, whichever user who called for the file creation. Otherwise, the file will be owned by the wrong user id. That means someone outside of the container without the right user ID won’t be able to work with the file. The user id mapping is by default handled using a user id of 1000, which is often right for single user Linux systems and allowing Docker to override the id.
Many Docker containers don’t need to care about this since they are targeted as running services with limited interaction on the filesystem level. But for build containers it would not be feasible to isolate the build results inside the container, preventing their use. This leads to the next important difference with many Docker containers: a build container needs to access the filesystem to import the sources that should be used in the build and export the results. Bridging the world of separate filesystems will be handled using a so called bind-mount.
Using Angular CLI inside the Docker container
Let’s start creating a simple Angular project using Angular CLI from the Docker container! The following Docker parameters will be used:
-v to specify the bind mount using the current working directory as base
-u override the default user id of 1000 to reflect the user currently executing the command
–rm instruct docker to remove the container after execution is complete as only the results in the filesystem are important
Angular CLI is provided as the ‘ng’ command, the parameter ‘new’ will create a new Angular project. Without docker we would execute just ng new MyDemo
Using the parameters from above we will execute
docker run -u $(id -u) --rm -v "$PWD":/app trion/ng-cli ng new MyDemo
The output shows that npm is used to download required libraries and the project is created.
Of course this Docker container can also be used to serve the application. To be able to access the application the port 4200 must be mapped from the outside to the container. Port binding is specified using the “-p” Docker parameter as shown below:
cd MyDemo docker run -u $(id -u) --rm -p 4200:4200 -v "$PWD":/app trion/ng-cli ng serve -host 0.0.0.0
In order to build the Angular project with the Docker container, ‘ng build’ can be used as execution target:
docker run -u $(id -u) --rm -v "$PWD":/app trion/ng-cli ng build
After completion the build results reside in the ‘dist’ folder.
To run unit tests a separate image is used. It is built on top of the first image and is therefore also able to perform the build step. Further it includes the chromium browser, enabling the Karma test runner to execute the unit tests. Inside the just created “MyDemo” directory the following command will execute the unit tests that were created as part of the project generation:
docker run -u $(id -u) --rm -v "$PWD":/app trion/ng-cli-karma ng test --watch false
Karma by default executes tests continuously whenever a change in the source files is detected. When using a native Docker, this works transparently. On systems that depend on a linux virtual machine to run Docker, like Windows, the file change notification does not work. To perform just a single test run the parameter “–watch false” is specified.
The final stage are end2end tests. They need a Java runtime in addition to be able to execute selenium driven tests, the trion/ng-cli-e2e image has the required dependencies. Angular CLI takes care of starting a local webserver and executing all tests when the e2e task is called.
docker run -u $(id -u) --rm -v "$PWD":/app trion/ng-cli-e2e ng e2e
Output from this command is shown in the following animation: