-1

I was wondering if someone could help me on this topic, I'm typing down some unit tests regarding to some functionalities that are done depending on the environment that's running at the moment.

This is my source code of the component I want to create the unit test:

import { Component, OnInit } from '@angular/core';

// DEV environment
import { environment } from '@env/environment';
// PROD environment
// import { environment } from '@env/environment.prod';
import { Logger } from '@app/core/logger/logger.service';
import { I18nService } from '@app/core/language/i18n.service';

let log: Logger;

@Component({
  selector: 'app-root',
   templateUrl: './app.component.html',
   styleUrls: ['./app.component.scss'],
   providers: [I18nService]
 })

export class AppComponent implements OnInit {

  constructor(private i18nService: I18nService) {
     log  = new Logger('X');
  }

  ngOnInit() {
    // Setup logger
    if (environment.isProduction) {
        Logger.enableProductionMode();
    }

    log.debug('init');

    // Setup translations
    this.i18nService.init(environment.defaultLanguage, environment.supportedLanguages);
  }

And and this is the unit testing:

 import * as environmentDEV from '@env/environment';
 import * as environmentPRO from '@env/environment.prod';
 ...

 let component: AppComponent;
 let fixture: ComponentFixture<AppComponent>;

 // Spies declarations
 let spy_initI8nServiceMethod: jasmine.Spy;
 let spy_debugLoggerAttr: jasmine.Spy;
 let spy_enableProductionModeLoggerMethod: jasmine.Spy;

 describe('AppComponent', () => {
       beforeEach(async(() => {
        TestBed.configureTestingModule({
          imports: [TranslateModule.forRoot()],
          declarations: [AppComponent],
          providers: [I18nService]
        });
        TestBed.compileComponents();
        fixture = TestBed.createComponent(AppComponent);
        component = fixture.debugElement.componentInstance;
       }));

       beforeEach(inject([I18nService],
                        (_i18nService: I18nService) => {

        i18nService = _i18nService;

        // Create spies
        // Spies
        spy_initI8nServiceMethod = spyOn(I18nService.prototype, 'init');
        spy_debugLoggerAttr = spyOn(Logger.prototype, 'debug');
        spy_enableProductionModeLoggerMethod = spyOn(Logger, 'enableProductionMode');
       }));

       it('should component init on PRO environment',
           async(() => {

        spyOn(environment, 'isProduction').and.returnValue(environmentPRO.environment.isProduction);
        spyOn(environment, 'defaultLanguage').and.returnValue(environmentPRO.environment.defaultLanguage);
        spyOn(environment, 'supportedLanguages').and.returnValue(environmentPRO.environment.supportedLanguages);

        component.ngOnInit();

        expect(spy_enableProductionModeLoggerMethod).toHaveBeenCalledBefore(spy_debugLoggerAttr);
        expect(spy_debugLoggerAttr).toHaveBeenCalledBefore(spy_initI8nServiceMethod);
        expect(spy_initI8nServiceMethod).toHaveBeenCalledWith(environmentPRO.environment.defaultLanguage,
                                                              environmentPRO.environment.supportedLanguages);
       }));
 });

My problem is that I'm unable to force constant environment to return certain values, I've both tried both spyOn & spyOnAttribute with same result. What's wrong here? shall I try another approach?

Andrés
  • 63
  • 2
  • 8
  • Properties cannot be `spyOn`, it is built for functions. I am a bit confused on what you wanted to test here. If the unit under test does not have to know about env changing (loading different envs depending on the unit's inputs), I would not consider testing different envs at all. – mixth Sep 25 '18 at 18:30
  • I just wanted to test for production environment that Logger.enableProductionMode(); statement is called when I've set unit test for production environment and not if it's DEV. In order to do that I have to force in the UT that environment.isProduction statement returns false. I know that spyOn is used for functions, but I've tried with spyOnProperty, but this can be used only for getter/setter params, which it's not the case. – Andrés Sep 26 '18 at 06:47
  • In that case, how about wrapping import statement into its own service? You can have `EnvironmentService.isProduction(): boolean` thus, isolating testing the logic from importing the environment configuration is possible. Btw, I normally use angular.json file to designate envs like this topic -> https://stackoverflow.com/questions/50313296/how-to-add-set-environment-angular-6-angular-json-file – mixth Sep 26 '18 at 10:57

1 Answers1

0

I've thought about this and I believe that maybe it could be a security leak to keep a configuration file as typescript (that will be converted into JavaScript in execution time) and it which looks like that, based on @mixth comments, can't be tested anyway so I'll follow your recommendations and I'll use a injectable service instead and json files to store settings. Thanks for the insight and stackoverflow's thread reference @mixth

Andrés
  • 63
  • 2
  • 8