Testing Angular
This guide offers tips and techniques for unit and integration testing Angular applications.
We can test our Angular applications from scratch by writing and executing pure javascript functions. Creating instances of the relevant classes, calling functions and checking the actual versus expected result.
But since testing is such a common activity with javascript there are a number of testing libraries and frameworks we can use which reduce the amount of time it takes to write tests.
Tools and frameworks that are used when testing Angular is :
Jasmine , Karma , Phantom-js , Istanbul, Sinon, Chai
we will use Jasmine and Karma to perform unit tests in Angular.
Why Is Unit Testing Required?
- Basically, it protects the existing code, which can be broken due to any changes.
2. Integrates an automatic build process to automate the pipeline of resource publishing at any time.
3. Clarifies what the code does, both when used as intended, and when faced with deviant conditions. Serves as a form of documentation for your code.
4. Reveals mistakes in design and implementation. When a part of the application seems hard to test, the root cause is often a design flaw, something to cure now rather than later when it becomes expensive to fix.
5. It allows us to test the interaction of directives or components with its template URL.
6. It allows us to easily track change detection.
7. It also allows us to use and test the Angular Dependency Integration framework.
Jasmin?
Jasmine is the most popular JavaScript testing framework in the Angular community. This is one the main features of Test Driven Development (TDD). This is the core framework that we will write our unit tests with. Basically, Jasmine and BDD try to describe a test method case in a human-readable pattern so that any user, including a non-technical person, can identify what is going on. Jasmine tests are written using JavaScript functions, which makes writing tests a nice extension of writing application code.
karma?
- Karma is a test automation tool for controlling the execution of our tests and what browser to perform them on. It also allows us to generate various reports on the results. For one or two tests, this may seem like overkill, but as an application grows larger and the number of units to test grows, it is important to organize, execute, and report on tests in an efficient manner. Karma is library-agnostic, so we could use other testing frameworks in combination with other tools (like code coverage reports, spy testing, e2e, etc.).
- In order to test our Angular application, we must create an environment for it to run in. We could use a browser like Chrome or Firefox to accomplish this (Karma supports in-browser testing), or we could use a browser less environment to test our application, which can offer us greater control over automating certain tasks and managing our testing workflow. PhantomJS provides a JavaScript API that allows us to create a headless DOM instance which can be used to bootstrap our Angular application. Then, using that DOM instance that is running our Angular application, we can run our tests.
- Karma is basically a tool which lets us spawn browsers and run Jasmine tests inside of them, which are executed from the command line. This results of the tests are also displayed in the command line. Karma also watches our development file changes and re-runs the tests automatically.
Difference between Jasmin & Karma?
Jasmin:
i] is a behavior-driven development framework for testing JavaScript code. It does not depend on any other JavaScript frameworks. It does not require a DOM. And it has a clean, obvious syntax so that you can easily write tests.
ii] Is a BDD framework for testing
iii] Is being tightly coupled with angular allows you to utilize the powerful dependency injection architecture that angular provides to effectively test in
Karma :
i] is essentially a tool for testing which spawns a web server that executes source code against test code for each of the browsers connected. The results of each test against each browser are examined and displayed via the command line to the developer such that they can see which browsers and tests passed or failed.
ii] Karma is test runner like grunt and gulp
iii] Karma is generic enough to allow you to use any testing framework to write your tests.
Initial Setup Required for Unit Tests
The angular CLI handles virtually all of the boilerplate code required to run tests. Let’s quickly demystify all of the testing boilerplate you would find in a new Angular app.
- karma.conf — Tells Karma how to run your tests.
2. protractor.conf — Tells proteactor how to run end-to-end tests
3. e2e — Your end-to-end tests are kept here
4. src/test.ts — recursively loads all the spec and framework files for testing
5. **.spec.ts — Anything you generate with the CLI includes a spec file where you define the actual tests.
Follow the below link to configuration:
Basic “Helllo World“ Example
Jasmine, and BDD in general, attempts to describe tests in a human readable format so that non-technical people can understand what is being tested. However even if you are technical reading tests in BDD format makes it a lot easier to understand what’s going on.
if we wanted to test “helloWorld” function:
function helloWorld() {
return ‘Hello world!’;
}
We would write a jasmine test spec like so:
describe(‘Hello world’, () => {
it(‘says hello’, () => {
expect(helloWorld())
.toEqual(‘Hello world!’);
});
});
The describe(string, function) function defines what we call a Test Suite, a collection of individual Test Specs.
The it(string, function) function defines an individual Test Spec, this contains one or more Test Expectations.
The expect(actual) expression is what we call an Expectation. In conjunction with a Matcher it describes an expected piece of behavior in the application.
The matcher(expected) expression is what we call a Matcher. It does a Boolean comparison with the expected value passed in vs. the actual value passed to the expect function, if they are false the spec fails.
Built-In Matchers
Below are the some of build in matchers
- expect(array).toContain(member);
2. expect(fn).toThrow(string);
3. expect(fn).toThrowError(string);
4. expect(instance).toBe(instance);
5. expect(mixed).toBeDefined();
6. expect(mixed).toBeFalsy();
7. expect(mixed).toBeNull();
8. expect(mixed).toBeTruthy();
9. expect(mixed).toBeUndefined();
10. expect(mixed).toEqual(mixed);
11. expect(mixed).toMatch(pattern);
12. expect(number).toBeCloseTo(number, decimalPlaces);
13. expect(number).toBeGreaterThan(number);
14. expect(number).toBeLessThan(number);
15. expect(number).toBeNaN();
16. expect(spy).toHaveBeenCalled();
17. expect(spy).toHaveBeenCalledTimes(number);
18. expect(spy).toHaveBeenCalledWith(…arguments);
Setup and Teardown (Basic Syntax)
Sometimes in order to test a feature we need to perform some setup, perhaps it’s creating some test objects. Also we may need to perform some cleanup activities after we have finished testing, perhaps we need to delete some files from the hard drive.
These activities are called setup and teardown (for cleaning up) and Jasmine has a few functions we can use to make this easier:
1] beforeAll
This function is called once, before all the specs in describe test suite are run.
2] afterAll
This function is called once after all the specs in a test suite are finished.
3] beforeEach
This function is called before each test specification, it function, has been run.
4] afterEach
This function is called after each test specification has been run.
What is Test Bed?
A Test Bed creates an Angular testing module, which is just a class an NgModule class. For example, notice how you have the declarations: [ demoComponent ] meta data just like any NgModule. This makes it possible to test your components in isolation.
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ demoComponent ]
})
.compileComponents();
}));
What is fixture?
In TDD, a test fixture creates a repeatable baseline for running tests. The beforeEach method initializes the demoComponent class in in the same way for each test. In this case, we also want to trigger change detection on the component with detectChanges. You can also trigger other lifecycle hooks here, such as destroy.
beforeEach(() => {
fixture = TestBed.createComponent(demoComponent);
component = fixture.componentInstance;
de = fixture.debugElement;
fixture.detectChanges();
});
we have a pattern that can be repeated before each test. Now let’s go over some of the most common tests you might need to write for a component.
Disabled and Focused tests
You can disable tests without commenting them our by just pre-pending x to the describe or it functions, like so:
xdescribe(‘Hello world’, () => {
xit(‘says hello’, () => {
expect(helloWorld())
.toEqual(‘Hello world!’);
});
});
These tests will not be run.
Conversely you can also focus on specific tests by pre-pending with f, like so:
fdescribe(‘Hello world’, () => {
fit(‘says hello’, () => {
expect(helloWorld())
.toEqual(‘Hello world!’);
});
});
Out of all the tests in all the tests suites and tests specs, these are the only ones that will be run.
Test case writing important points
- All imports should be present in your spec file.
2. Create all services stub whichever used.
3. Create mock data of each services.
4. While creating any service stub verify the return data type. (I.e. string , observable, subjects etc. )
5. While creating the mock data verify the data type.
6. Give proper name while writing the test cases.
7. View of one component is used in other component in that case make sure that all the imports , services related to other component should be taken into consideration.
8. Date format should be proper while making the mock data.