Unable to run unit tests in C8Y applications

I am trying to run tests on a web application I built with the C8Y CLI but I am running into a couple of problems.

I have organized my source code into two folders: app and test. The former stores all of my application code and the latter is reserved for executing unit tests on it.

src
 ├── app
       ├── live-dashboard
            ├── live-dashboard.component.html
            ├── live-dashboard.component.less
            ├── live-dashboard.component.ts
            ├── live-dashboard.factory.ts
            ├── live-dashboard.module.ts
            ├── live-dashboard.service.ts
 ├── test
      ├── live-dashboard.component.spec.ts

Here is my test file:

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { BrowserDynamicTestingModule } from '@angular/platform-browser-dynamic/testing';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA } from '@angular/core';

import { LiveDashboardComponent} from 'src/app/live-dashboard/live-dashboard.component'
import { LiveDashboardService } from 'src/app/live-dashboard/live-dashboard.service';

describe('LiveDashboardComponent test', () => {
    let fixture: ComponentFixture<LiveDashboardComponent>;
    let component: LiveDashboardComponent;
    let service: LiveDashboardService

    // Init Angular Test Environment
    beforeAll(() => {
        TestBed.initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamic())
    });

    // Configure Testing Module
    beforeEach(() => {
        TestBed.configureTestingModule({
            declarations: [
                LiveDashboardComponent
            ],
            providers: [
                LiveDashboardService
            ],
            schemas: [
                CUSTOM_ELEMENTS_SCHEMA,
                NO_ERRORS_SCHEMA
            ]
        }).compileComponents();  // necessary for when our components use templateUrl 
                                 // and styleUrls to load external files (css and html) 
                                 // that compiler has to read asynchronously.
    })

    // Configure component and service(s)
    beforeEach(() => {
        fixture = TestBed.createComponent(LiveDashboardComponent);
        component = fixture.componentInstance;
        fixture.detectChanges(); // component will start with ngOnInit
        service = fixture.debugElement.injector.get(LiveDashboardService);
    })

    test('Component should be defined', () => {
        expect(component).toBeDefined();
    })

    test.skip('Test the getModeFragmentSeries', () => {

    })

    afterEach(() => {
        fixture.destroy(); // Destroys the fixture and its contents
        component = null; // Resets the component reference
        fixture = null; // Resets the fixture reference
      });
});

When I try to run npm test, however, I keep getting an error stating that the test file cannot find the

 FAIL  src/app/live-dashboard/live-dashboard.component.spec.ts
  ● Test suite failed to run

    Cannot find module 'src/app/live-dashboard/live-dashboard.component' from 'src/app/live-dashboard/live-dashboard.component.spec.ts'

      4 | import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA } from '@angular/core';      
      5 |
    > 6 | import { LiveDashboardComponent} from 'src/app/live-dashboard/live-dashboard.component'
        | ^
      7 | import { LiveDashboardService } from 'src/app/live-dashboard/live-dashboard.service';
      8 |
      9 | describe('LiveDashboardComponent test', () => {

      at Resolver._throwModNotFoundError (node_modules/jest-resolve/build/resolver.js:491:11)
      at Object.<anonymous> (src/app/live-dashboard/live-dashboard.component.spec.ts:6:1)

Test Suites: 2 failed, 2 total
Tests:       0 total
Snapshots:   0 total
Time:        5.933 s
Ran all test suites.

I have tried moving the test file to the app folder but I just got the same response.

In addition, I am getting another error, this time with my first test block stating that the property 'toBeDefined' does not exist on type 'Assertion'.ts(2339).

I hovered over the expect method and discovered that it is using the expect method from the Chai framework:
var expect: Chai.ExpectStatic (val: any, message?: string) => Chai.Assertion

How can this be if the project is already set up to use Jest as its testing framework? How would I get it to use Jest’s expect method instead?

Hi Lucas,

I guess you need to change your imports to relative paths. So instead of src/app/live-dashboard/live-dashboard.component use ../app/live-dashboard/live-dashboard.component.

Regarding the types:
Did you install @types/jest as a devDependency in you project?
Did you mention jest in the compilerOptions.types array of your tsconfig.spec.json ?

Regards,
Tristan

Hi Tristan,

I changed the relative imports to relative paths and that fixed the first issue.

On the other hand, I had checked the devDependency and the ts.config.spec.json file beforehand and they had the same configuration you mentioned.

package.json

"devDependencies": {
    "@angular-devkit/build-angular": "14.0.6",
    "@angular/compiler-cli": "14.0.6",
    "@angular/language-service": "14.0.6",
    "@angular/localize": "14.0.6",
    "@angular/service-worker": "14.0.6",
    "@c8y/cli": "1018.0.211",
    "@jest/types": "^29.6.3",
    "@types/jest": "^28.1.6",
    "ajv": "^7.2.4",
    "cypress": "^13.1.0",
    "jest-preset-angular": "^12.2.0",
    "typescript": "4.7.4"
  },

tsconfig.spec.json

{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "outDir": "./out-tsc/spec",
    "types": ["jest"],
    "esModuleInterop": true
  },
  "include": ["src/**/*.spec.ts", "src/**/*.d.ts"]
}

Update: I properly configured my test file and the test runs without errors, even though the toBeDefined error still appears underlined in red.

This is what my final code looks like:

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA } from '@angular/core';

import { LiveDashboardComponent} from '../../app/live-dashboard/live-dashboard.component'
import { LiveDashboardService } from '../../app/live-dashboard/live-dashboard.service';
import { FormBuilder } from '@angular/forms';
import { MeasurementService } from '@c8y/client';

describe('LiveDashboardComponent test', () => {
    let fixture: ComponentFixture<LiveDashboardComponent>;
    let component: LiveDashboardComponent;
    let service: LiveDashboardService

    // Configure Testing Module
    beforeEach(async () => {
        TestBed.configureTestingModule({
            declarations: [LiveDashboardComponent],
            providers: [LiveDashboardService, FormBuilder, MeasurementService],
            schemas: [
                CUSTOM_ELEMENTS_SCHEMA,
                NO_ERRORS_SCHEMA
            ]
        }).compileComponents();
    })

    // Configure component and service(s)
    beforeEach(() => {
        fixture = TestBed.createComponent(LiveDashboardComponent);
        component = fixture.componentInstance;
        fixture.detectChanges(); // triggers Angular change detection
        service = fixture.debugElement.injector.get(LiveDashboardService);
    })

    afterEach(() => {
        fixture.destroy(); // Destroys the fixture and its contents
        component = null; // Reset the component reference
        fixture = null; // Reset the fixture reference
    });

    test('Component should be defined', () => {
        expect(component).toBeDefined();
    })
});

I turns out I had to remove the import statements for BrowserDynamicTestingModule and platformBrowserDynamic as the test environment is already being initialized in the index.ts file.