简体   繁体   中英

Angular Testing - how to test Data Service with extended base class

I'm having a hard to setup Tests of my Data Services, I'm trying to use the HttpClientTestingModule as defined in these articles Testing with the Angular HttpClient API , from this SO Question and also tried inject from this article Testing HttpClient but I can't get it to work.

I believe my problems comes from the fact that I have a global DataService which uses 3 service providers ( HttpClient , NGXLogger and TranslateService from ngx-translate ). Then I have multiple Data Services which extends the global DataService like so

@Injectable()
export class UserDataService extends DataService {
  constructor(protected http: HttpClient, protected logger: NGXLogger, protected translate: TranslateService) {
  super(http, logger, translate);
  this.url = `api/users`;
}

getUsers(): Observable<Users[]> {
  return super.getAll<User[]>(this.url);
}

So as you can see, each extended data services (like UserDataServices ) takes care of injecting the 3 necessary dependencies to the super call.

I'm trying to code my test, which is done with Jest , with the following code

describe('User Data Service', () => {
  let dataService: TemplateDataService;
  let httpTestingController: HttpTestingController;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [
        HttpClientTestingModule,
        TranslateModule.forRoot(),
        LoggerModule.forRoot({ level: NgxLoggerLevel.ERROR, serverLoggingUrl: 'api/logging', serverLogLevel: NgxLoggerLevel.ERROR }),
      ],
      providers: [
        UserDataService
      ]
    });
    dataService = TestBed.get(UserDataService);
    httpTestingController = TestBed.get(HttpTestingController);
  });

it('should return an Observable<User[]>', () => {
  const dummyUsers = [
    { login: 'John' },
    { login: 'Doe' }
  ];

  dataService.getAll().subscribe((users: User[]) => {
    expect(users.length).toBe(2);
    expect(users).toEqual(dummyUsers);
  });

  const req = httpTestingController.expectOne(`api/users`);
  expect(req.request.method).toBe('GET');
  req.flush(dummyUsers);
});

and it keeps throwing the following error

Unexpected value 'HttpClientModule' imported by the module 'HttpClientTestingModule'. Please add a @NgModule annotation.

I also tried with this piece of code, but I get the same error

it(
  'should get users',
  inject(
    [HttpTestingController, NGXLogger, TranslateService, DataService],
    (
      httpMocker: HttpTestingController,
      logger: NGXLogger,
      translate: TranslateService,
      dataServicer: DataService
    ) => {
      // ...our test logic here
    }
  )
);

I'm fairly new to Unit Testing (I started last week) and I'm totally blocked with testing any of my Data Services (on the bright side, I'm ok with testing the component with a data service mock, but I'd like to test these data services as well).

On the technology side, the project uses Angular: ^7.2.10 and jest: ^23.6.0 .

EDIT

I forgot to mention that DataService is also a Service and I also tried to add it to the providers in the beforeEach() and I still get the same error ( Unexpected value 'HttpClientModule'... ). As shown below

beforeEach(() => {
  TestBed.configureTestingModule({
    imports: [
      HttpClientTestingModule,
      TranslateModule.forRoot(),
      LoggerModule.forRoot({ level: NgxLoggerLevel.ERROR, serverLoggingUrl: 'api/logging', serverLogLevel: NgxLoggerLevel.ERROR }),
    ],
    providers: [
      DataService,
      UserDataService
    ]
  });
});

Also to give a bit more overview of the DataService , here's a strip down version of it.

export class DataService {
  constructor(protected http: HttpClient, protected logger: NGXLogger, protected translate: TranslateService) {}

  get<T>(id?: string, options?: HttpOptions): Observable<T> {
    const caller = findCaller(new Error());
    const uriWithId = id ? `/${id}` : '';
    const requestUrl = this.addGlobalQueryParams(`${this.url}${uriWithId}`);
    this.logger.debug(`DataService - GET method [${caller}] - START - URL:: ${requestUrl}`);

    return this.http.get<T>(requestUrl, options).pipe(map((response: any) => {
      this.logger.debug(`DataService - GET method [${caller}] - END - URL:: ${requestUrl} - Response:: `, response);
      return response;
    }));
  }

  getAll<T>(url?: string, options?: HttpOptions): Observable<T> {
    const caller = findCaller(new Error());
    if (url) {
      this.logger.debug(`DataService - POST method [${caller}] - START - URL:: `, url);
      return this.http.post<T>(url).pipe(map((response: any) => {
        this.logger.debug(`DataService - POST method [${caller}] - END - URL:: ${url} - Response:: `, response);
        return response;
      }));
    }
  }
}

EDIT 2

I guess what I'm looking for is how to Unit Test an Extended Class, which was actually asked in the Angular repo here but was never answered and closed because it wasn't an issue but more of a question.

Firstly, you shouldn't need to do the following in your test.

 providers: [
   UserDataService
 ]

Since you are testing the UserDataService itself, doing the following after setting the TestBed should be enough.

dataService = TestBed.inject(UserDataService); //TestBed.get(UserDataService);

The other thing to change would be making your base class injectable.

@Injectable()
export class DataService {...}

I know it sounds stupid, but apparently angular DI seems to need that too.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM