0
votes

i have many components with angular 9, i want to test that the content and functions are ok but when i run tests on "ng test" they fail because they expect providers but found none.

This is one example component.ts

import {Component}
import {AppComponent} 
import {AdminService}
import {GenUtil}
import {UserUtil} 
import {TableComponent} 
import {UserRole} 


export class AdminListComponent extends TableComponent<any> {

  currentUserRole: number = UserRole.USER.level;

  userRoles: UserRole = UserRole;

  constructor(
      private adminService: AdminService, 
      private app: AppComponent) {
    super();
    this.loadData();
    app.showBackoffice();
  }

  getTableSettings() {
    return [
      {
        objectKey: '_id',
        sort: 'desc'
      }, {
        objectKey: 'is_company'
      }, {
        objectKey: 'personal_id_type'
      }, {
        objectKey: 'personal_id_number'
      }, {
        objectKey: 'name'
      }, {
        objectKey: 'last_login_date'
      }, {
        objectKey: 'last_modification_date'
      }, {
        objectKey: 'enabled'
      }, {
        objectKey: 'accredited_accept_terms'
      }
    ]
  }

  getTableFields() {
    return [{
      name: 'Email',
      objectKey: '_id'
    }, {
      name: 'Tipo de usuario',
      objectKey: 'is_company',
      value: row => UserUtil.getUserTypeHumanized(row),
      render: row => UserUtil.getUserTypeHumanized(row)
    }, {
      name: 'Tipo de identificación',
      objectKey: 'personal_id_type',
      value: row => UserUtil.getPersonalIDTypeHumanized(row),
      render: row => UserUtil.getPersonalIDTypeHumanized(row)
    }, {
      name: 'Número de identificación',
      objectKey: 'personal_id_number',
      value: row => UserUtil.getPersonalIDHumanized(row),
      render: row => UserUtil.getPersonalIDHumanized(row)
    }, {
      name: 'Nombre',
      objectKey: 'name'
    }, {
      name: 'Último login',
      objectKey: 'last_login_date',
      value: row => row.last_modification_date ? row.last_login_date : "Nunca",
      render: row => row.last_modification_date ? GenUtil.formatTimestamp(new Date(row.last_login_date)) : "Nunca"
    }, {
      name: 'Fecha de modificación',
      objectKey: 'last_modification_date',
      value: row => row.last_modification_date ? row.last_modification_date : "Nunca",
      render: row => row.last_modification_date ? GenUtil.formatTimestamp(new Date(row.last_modification_date)) : "Nunca"
    }, {
      name: '',
      objectKey: 'enabled',
      render: row => this.currentUserRole == UserRole.USER.level ? '<a class="btn btn-success">Promocionar</a>' : '<a class="btn btn-warning">Degradar</a>',
      click: (row) => this.changeUserRole(row._id, this.currentUserRole == UserRole.USER.level ? "30" : "00")
    }, {
      name: '',
      objectKey: 'accredited_accept_terms',
      render: row => '<a class="btn btn-success">Ver usuario</i></a>',
      click: (row) => {
        if (window.localStorage) {
          localStorage.setItem('id', row._id);
          let bd;
          switch (row.is_verified_bd) {
            case true:
              bd = 1;
              break;
            case false:
              bd = 0;
              break;
            case null:
              bd = 2;
              break;
            case undefined:
              bd = 2;
              break;
            default:
              bd = 2;
          }
          let accredited;
          switch (row.accredited_accept_terms) {
            case true:
              accredited = 1;
              break;
            case false:
              accredited = 0;
              break;
            default:
              accredited = 0;
          }
          localStorage.setItem('bd', bd);
          localStorage.setItem('accredited', accredited);
        }
        window.open(`user-history`)
      }
    }]
  }

  loadData() {
    let searchConditions = {
      role: UserRole.encodeRol(this.currentUserRole)
    };

    this.adminService.getAdminList(searchConditions)
      .then((response) => {

        if (response.status == "ok") {
          this.data = response.data.users_short_info;

          if (this.data.length == 0) {
            this.info("No se han encontrado usuarios con rango de Administrador")
          }

          this.buildTable();
        } else {
          this.error("No ha sido posible recuperar los usuarios", response)
        }

      })
  }

  changeUserRole(email, role) {
    this.adminService.changeToUserOrAdmin(email, role)
      .then((response) => {
        if (response.status == "ok") {
          this.success(`Se ha ${role == "30" ? 'promocionado' : 'degradado'} a ${email}`)
          this.loadData();
        } else {
          this.error(`Ha ocurrido un error ${role == "30" ? 'promocionando' : 'degradando'} a ${email}`)
        }
      });
  }

}

This is my AdminService.ts

import { Injectable }
import { BaseService }
import { Http }
import { environment }
import { Router }

@Injectable()
export class AdminService extends BaseService {

  constructor(protected http: Http, public router: Router) {
    super(http, router)
  }

  getAllProjects(): Promise<any> {
    let url: string = `${this.BASE_URL}/api/`;
    return this.post(url, {})
  }
}

This is my spec.ts generated by angular

import { async, ComponentFixture, TestBed } 

import { AdminListComponent } 
import { AdminService } 
import { HttpModule, Http } 
import { AppModule } 
import { Router }
import { AppComponent }

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

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ AdminListComponent ],
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(AdminListComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

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

And when i run my test with ng test (selecting only this component) ive got:

  • Should Create -> SuperAdminListComponent > should create NullInjectorError: R3InjectorError(DynamicTestModule)[AdminService -> AdminService]: NullInjectorError: No provider for AdminService! --- > Expected undefined to be truthy.
  • Should Create -> NullInjectorError: R3InjectorError(DynamicTestModule)[AdminService -> AdminService]: NullInjectorError: No provider for AdminService! --- > Expected undefined to be truthy.

This is the image of the errors

Karma errors

this is my package.json

{
  "name": "bdkfrontscss",
  "version": "0.0.0",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e"
  },
  "private": true,
  "dependencies": {
    "@agm/core": "^1.1.0",
    "@angular-generic-table/core": "^4.17.1",
    "@angular/animations": "~9.0.3",
    "@angular/cdk": "^9.1.0",
    "@angular/common": "~9.0.3",
    "@angular/compiler": "~9.0.3",
    "@angular/core": "~9.0.3",
    "@angular/forms": "~9.0.3",
    "@angular/http": "^7.2.16",
    "@angular/platform-browser": "~9.0.3",
    "@angular/platform-browser-dynamic": "~9.0.3",
    "@angular/router": "~9.0.3",
    "@ngx-translate/core": "^12.1.2",
    "@ngx-translate/http-loader": "^4.0.0",
    "@swimlane/ngx-charts": "^13.0.2",
    "angular-froala-wysiwyg": "^3.1.0",
    "angular-generic-table": "^4.0.1",
    "angularx-qrcode": "^2.1.0",
    "aos": "^2.3.4",
    "bootstrap": "^3.4.1",
    "cookieconsent": "^3.1.1",
    "include-media": "^1.4.9",
    "jquery": "^3.4.1",
    "json-style-converter": "^1.0.3",
    "jspdf": "^1.5.3",
    "moment": "^2.24.0",
    "ng-recaptcha": "^5.0.0",
    "ng2-device-detector": "^1.0.1",
    "ng4-intl-phone": "^1.2.1",
    "ngx-bootstrap": "^5.5.0",
    "ngx-cookie-service": "^2.4.0",
    "ngx-cookieconsent": "^2.2.3",
    "ngx-currency": "^2.2.2",
    "ngx-currency-mask": "^4.3.2",
    "ngx-device-detector": "^1.3.20",
    "ngx-loading": "^8.0.0",
    "ngx-quill": "^8.0.0",
    "ngx-sharebuttons": "^4.1.4",
    "ngx-toastr": "^12.0.0",
    "rxjs": "~6.5.4",
    "rxjs-compat": "^6.5.4",
    "tether": "^1.4.7",
    "tslib": "^1.10.0",
    "zone.js": "~0.10.2"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "~0.900.4",
    "@angular/cli": "~9.0.4",
    "@angular/compiler-cli": "~9.0.3",
    "@angular/language-service": "~9.0.3",
    "@types/node": "^12.11.1",
    "@types/jasmine": "~3.5.0",
    "@types/jasminewd2": "~2.0.3",
    "codelyzer": "^5.1.2",
    "jasmine-core": "~3.5.0",
    "jasmine-spec-reporter": "~4.2.1",
    "karma": "~4.3.0",
    "karma-chrome-launcher": "~3.1.0",
    "karma-coverage-istanbul-reporter": "~2.1.0",
    "karma-jasmine": "~2.0.1",
    "karma-jasmine-html-reporter": "^1.4.2",
    "protractor": "~5.4.3",
    "ts-node": "~8.3.0",
    "tslint": "~5.18.0",
    "typescript": "~3.7.5"
  }
}

If i try to put on providers:[AdminService, Http, Router, ConnectionBackend, RequestOptions] i finally got another error "Error: This constructor was not compatible with Dependency Injection. "

Thanks

1

1 Answers

0
votes

You need to mock adminService and appComponent it seems, I am not sure how you are injecting a component in the constructor though.

Try this:

import { async, ComponentFixture, TestBed } 

import { AdminListComponent } 
import { AdminService } 
import { HttpModule, Http } 
import { AppModule } 
import { Router }
import { AppComponent }

describe('AdminListComponent', () => {
  let component: AdminListComponent;
  let fixture: ComponentFixture<AdminListComponent>;
  let mockAdminService: any;
  let mockAppComponent: any;

  beforeEach(async(() => {
    // first argument in createSpyObj is optional and will be displayed in case of errors
    // second argument is an array of the public methods that will be mocked
    mockAppComponent = jasmine.createSpyObj('appComponent', ['showBackoffice']);
    mockAdminService = jasmine.createSpyObj('adminService', ['getAdminList', 
    'changeToUserOrAdmin' ]);
    TestBed.configureTestingModule({
      declarations: [ AdminListComponent ],
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(AdminListComponent);
    component = fixture.componentInstance;
    mockAdminService.getAdminList.and.returnValue(Promise.resolve([/* put here however the admin list should be mocked to/look like */]));
    fixture.detectChanges();
  });

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

A good class on Angular unit testing is on PluralSight (https://app.pluralsight.com/library/courses/unit-testing-angular/table-of-contents). Check it out.