robisim74 / angular-l10n

Angular library to translate texts, dates and numbers
MIT License
380 stars 59 forks source link

Unit tests question #322

Closed ihor-zinchenko closed 3 years ago

ihor-zinchenko commented 3 years ago

Hi! tell me please how correctly write tests with your module? i've got error

NullInjectorError: R3InjectorError(DynamicTestModule)[InjectionToken L10N_LOCALE -> InjectionToken L10N_LOCALE]:
          NullInjectorError: No provider for InjectionToken L10N_LOCALE!
        error properties: Object({ ngTempTokenPath: null, ngTokenPath: [ 'InjectionToken L10N_LOCALE', 'InjectionToken L10N_LOCALE' ] })
        NullInjectorError: R3InjectorError(DynamicTestModule)[InjectionToken L10N_LOCALE -> InjectionToken L10N_LOCALE]:
          NullInjectorError: No provider for InjectionToken L10N_LOCALE!

in default test

robisim74 commented 3 years ago

Hi @ihor-zinchenko ,

you have to provide the library modules such as L10nTranslationModule. Here an example: https://github.com/robisim74/angular-l10n/blob/master/projects/angular-l10n-app/src/app/app.component.spec.ts

ihor-zinchenko commented 3 years ago

@robisim74 great! thanks a lot!) Could you please also give me advice about next case: i want to write test to my function:

public setLocale(locale: L10nLocale): Promise<void> {
    return this.translation.setLocale(locale);
  }

i wrote next code:

 it('should setLocale() set language to english', fakeAsync(() => {
    const mockLocale = {language: 'en', currency: 'USD', timeZone: 'America/Los_Angeles'};

    component.setLocale(mockLocale).then(res => {
      expect(component.locale).toEqual(jasmine.objectContaining({
        language: 'en', currency: 'USD', timeZone: 'America/Los_Angeles'
      }));
    });
  }));

but i've got next error

Expected $.language = '' to equal 'en'.
        Expected $.currency = undefined to equal 'USD'.
        Expected $.timeZone = undefined to equal 'America/Los_Angeles'.
        Error: Expected $.language = '' to equal 'en'.
        Expected $.currency = undefined to equal 'USD'.
        Expected $.timeZone = undefined to equal 'America/Los_Angeles'.

After investigation i found next that locale default value is {language: ''} and i can't find solution how to solve that

robisim74 commented 3 years ago

@ihor-zinchenko ,

if add in in the sample app test (link above) this:

    it('should set the locale', () => {
        app.setLocale({ language: 'en-US', currency: 'USD', timeZone: 'America/Los_Angeles' });

        fixture.detectChanges();

        expect(app.locale).toEqual(jasmine.objectContaining({
            language: 'en-US', currency: 'USD', timeZone: 'America/Los_Angeles'
        }));
    });

it works without problems.

In your case, the locale has the default value (while it should already be initialized). So you should check that the app and library are ready when you run that test, and that the library initialization doesn't fail for some reason. Try to add to you component the following subscriber:

        this.translation.onError().subscribe({
            next: (error: any) => {
                if (error) console.log(error);
            }
        });

and try to check if there is any error.

ihor-zinchenko commented 3 years ago

@robisim74 yes it works but incorrect, it looks like app.setLocale({ language: 'en-US', currency: 'USD', timeZone: 'America/Los_Angeles' }); is never called because app.local always {langugage: ''}

ihor-zinchenko commented 3 years ago

About errors i've got image

robisim74 commented 3 years ago

So when the test starts, translation data are not loaded, and the locale is not initialized.

If you are loading real json files, you have to change the settings in karma config, otherwise it is better to use mock data in tests (as in the example app).

ihor-zinchenko commented 3 years ago

@robisim74 thank you for your answer, yes i loading real json files, but i can't find where you use mock data in tests in your example only one test file https://github.com/robisim74/angular-l10n/blob/master/projects/angular-l10n-app/src/app/app.component.spec.ts

robisim74 commented 3 years ago

Here: https://github.com/robisim74/angular-l10n/blob/master/projects/angular-l10n-app/src/app/app.component.spec.ts#L11-L19

And you can notice that in the test I don't pass the HttpTranslationLoaderof the json files.

ihor-zinchenko commented 3 years ago

this is works

const config: L10nConfig = {
  format: 'language-region',
  providers: [
      {
        name: 'app',
        asset: {
          en,
          ru
        },
        options: { version: '11.0.0' }
      }
  ],
  fallback: false,
  cache: true,
  keySeparator: '.',
  defaultLocale: { language: 'en', currency: 'USD', timeZone: 'America/Los_Angeles' },
  schema: [
      { locale: { language: 'en', currency: 'USD', timeZone: 'America/Los_Angeles' }, dir: 'ltr', text: 'United States' },
      { locale: { language: 'ru', currency: 'RUB', timeZone: 'Europe/Rome' }, dir: 'ltr', text: 'Русский' }
  ],
  defaultRouting: true
};

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

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [
          RouterTestingModule,
          L10nTranslationModule.forRoot(config),
          L10nIntlModule
      ],
      declarations: [
        HeaderComponent
      ]
    }).compileComponents();

    const loader = TestBed.inject(L10nLoader);
    await loader.init();
  });

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

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

  it('should setLocale() set language to english', fakeAsync(() => {
    const mockLocale = {language: 'en', currency: 'USD', timeZone: 'America/Los_Angeles'};
    component.setLocale(mockLocale).then(res => {
      expect(component.locale).toEqual(jasmine.objectContaining({
        language: 'en', currency: 'USD', timeZone: 'America/Los_Angeles'
      }));
    });
  }));

  it('should setLocale() set language to russian', fakeAsync(() => {
    const mockLocale = { language: 'ru', currency: 'RUB', timeZone: 'Europe/Rome' };
    component.setLocale(mockLocale).then(res => {
      expect(component.locale).toEqual(jasmine.objectContaining({
        language: 'ru', currency: 'RUB', timeZone: 'Europe/Rome'
      }));
    });
  }));
});

thank you! )