3

I'm new to Angular and attempting to test an Angular 6 service which utilizes handlebars.js to build client-side html templates, based on input JSON / data.

The service produces the desired result in development, but I'm unable to build a test confirming the service compiles html correctly. I have base templates (as JavaScript strings) imported directly into the service, but when I attempt to run the compilation method in the .spec file, I receive Error: You must pass a string or Handlebars AST to Handlebars.compile. You passed undefined, meaning the base template string variables are not being instantiated in the service within the .spec file.

PdfReportService.service.ts file

import { Injectable } from '@angular/core';
import * as Handlebars from 'handlebars/dist/handlebars.min.js';

// base template
import base_html from './root_template/html';
// form templates
import form_html from './form_template/form.html';
import form_css from './form_template/form.css';

@Injectable({
  providedIn: 'root'
})
export class PdfReportService {
  ...
  ...

  public compileform(json: object, context?: any) {
    Handlebars.registerPartial({ hbs_css_template: form_css });
    Handlebars.registerPartial({ hbs_form_body: form_html });
    return Handlebars.compile(base_html)({ json, context });
  }
}

PdfReportService.service.spec.ts file

import { TestBed, inject } from '@angular/core/testing';
import { PdfReportService } from './pdf-reports.service';
// import test data
import {
  formInputJson,
  formInputContext,
  formCompiledHTML
} from './pdf-reports.service.spec.data';

fdescribe('PdfReportService', () => {
  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [PdfReportService]
    });
  });

  it('should compile the correct html from form input data', inject(
    [PdfReportService],
    (service: PdfReportService) => {
      const result = service.compileform(formInputJson, formInputContext);
      expect(result).toEqual(formCompiledHTML);
    }
  ));
});

I've tried altering the service and multiple different methods of passing the vars through the test, but they always remain undefined. Any help would be appreciated!

Andrew
  • 1,406
  • 1
  • 15
  • 23

1 Answers1

1

The typical methodology used in unit testing is just to make sure your code is doing what it supposed to do, not test out data returned from third party libraries. Therefore I would suggest spying on the methods of Handlebars so they aren't actually called during testing, but you can test how your service tries to call them.

Based on what you wrote above, I set up a stackblitz testing environment to try out a few ideas. It is here: https://stackblitz.com/edit/stackoverflow-q-53075746?file=app%2Fpdf-reports.service.spec.ts. Feel free to fork this into your own stackblitz account and play around with it - I didn't have any data to put into the various test files and don't know Handlebars well enough to import it properly. Still, I was able to get a working test of your service.

Essentially what I did in there was import Handlebars inside the spec file so I could spy on it's methods, then change the spec to the following:

it('should compile the correct html from form input data', inject(
  [PdfReportService],
  (service: PdfReportService) => {
    const returnFunc = jasmine.createSpy('returnFunc').and.returnValue(formCompiledHTML);
    const regPartialSpy = spyOn(Handlebars, 'registerPartial');
    const compileSpy = spyOn(Handlebars, 'compile').and.returnValue(returnFunc);
    const result = service.compileform(formInputJson, formInputContext);
    expect(regPartialSpy).toHaveBeenCalledTimes(2);
    expect(compileSpy).toHaveBeenCalledWith({/* base_html */});
    expect(returnFunc).toHaveBeenCalledWith({ json: jasmine.any(Function), context: jasmine.any(Function) });
    expect(result).toEqual(formCompiledHTML);
  }));
});

I hope this is helpful.

dmcgrandle
  • 5,934
  • 1
  • 19
  • 38