0

I have been struggling for a long time with trying to mock one function I want to test. I have been reading the Jest documentation and I'm sure the answer probably is there somewhere but I just can't but the pieces together to make it work. I also couldn't find any previous question that made me solve my issue as many of the ones I looked into seems a bit outdated.

This is the function I'm trying to test (a bit simplified):

export function getOrganizationAdministrationLinks(permissions: RolePermission[], features: FeatureEnum[],
    translateService: CustomTranslateService): Array<{ name: string, target: string } | 'divider'> {
const uiLinks = new Array<{ name: string, target: string }>();
const organizationLinks = new Array<{ name: string, target: string }>();
const tradingLinks = new Array<{ name: string, target: string }>();

if (havePermissionsAndFeatures(permissions, features, OrganizationSettingsPermissions.permissions, OrganizationSettingsPermissions.features)) {
    uiLinks.push({ name: translateService.get('something'), target: 'interface' });
}

return [uiLinks, organizationLinks, tradingLinks]
    .filter(group => group.length > 0)
    .flatMap(group => [...group, 'divider' as const])
    .slice(0, -1);

The dependency I'm having issues with is the CustomTranslateService one. It looks like this:

export class CustomTranslateService {

public onLangChange: EventEmitter<LangChangeEvent>;

constructor(private readonly translate: TranslateService,
            private readonly userBrowserStateService: UserBrowserStateService) {
    this.onLangChange = this.translate.onLangChange;
    this.setCulture(this.userBrowserStateService.getCulture());
}
/**
 * Returns a translation for the translation key & params.
 */
public get(key: I18nKey): string {
    if (typeof key === 'string') {
        return this.translate.instant(key);
    } else {
        return this.translate.instant(key.key, key.params);
    }
}

The part I'm struggling with is that when calling getOrganizationAdministrationLinks which is the function I want to test, I have to pass a CustomTranslateService. The CustomTranslateService itself has dependencies and I can't figure out how to mock this. I suspect that I might have to mock it manually but it feels a bit overkill since I just want to mock the return call of the get method.

I'm only using jest and jest-preset-angular packages. I've seen some suggestions of using ts-jest but I'm not sure if it's needed. Also I'm on Angular 12.0.2 and TypeScript 4.1.3.

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
jagge123
  • 263
  • 1
  • 5
  • 15
  • 1
    _"The `CustomTranslateService` itself has dependencies and I can't figure out how to mock this"_ - the point of using a test double for the collaborator is that you don't have to care what its dependencies are, you isolate what you're actually testing from it. Why not just e.g. `useValue: { get: () => 'something translated' }` as the test double? – jonrsharpe Nov 08 '21 at 08:45
  • @jonrsharpe But i still need to pass CustomTranslateService to the getOrganizationAdministrationLinks-function in my test, and this requires an instance of the CustomTranslateService? – jagge123 Nov 08 '21 at 08:55
  • 1
    No it doesn't, it just requires something that can play that role. Angular has its own DI system to help you with this, or in this case you just pass it as an argument. – jonrsharpe Nov 08 '21 at 08:59
  • @jonrsharpe The part about passing it as an argument is where is struggle. How do i get an object that i can pass? All the examples i see is just mocking a class without getting an instance that can be used to pass to the method? Thanks. – jagge123 Nov 08 '21 at 09:47
  • 1
    `getOrganizationAdministrationLinks([], [], { get: () => 'something translated' } as CustomTranslateService)`? – jonrsharpe Nov 08 '21 at 09:48
  • That gives me -> "Conversion of type '{ get: () => string; }' to type 'CustomTranslateService' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. Type '{ get: () => string; }' is missing the following properties from type 'CustomTranslateService': onLangChange, translate, userBrowserStateService" – jagge123 Nov 08 '21 at 09:53
  • OK, and that _was_ intentional - you're creating a test double, the thing you're testing doesn't use those other properties (and according to the definition you've shown, `translate` and `userBrowserStateService` are private anyway). Read e.g. https://stackoverflow.com/a/69771198/3001761 for some of the other options around type-safe test doubles. – jonrsharpe Nov 08 '21 at 09:55
  • @jonrsharpe Thanks alot for ur help. I decided to go with creating a partial of the translateService. – jagge123 Nov 09 '21 at 06:44

1 Answers1

0

This is a basic test with the solution for what i wanted to achive:

    test('getOrganizationAdministrationLinks should return correct name and target when called with rolePermissions and features', () => {
    const expected = [{
        name: 'overruns',
        target: 'interface'
    }];

    const translateService: Partial<CustomTranslateService> = { get: () => 'overruns' } ;
    const actual = getOrganizationAdministrationLinks(rolePermissions, features, translateService as CustomTranslateService);

    expect(actual).toEqual(expected);
});

Thank you @jonrsharpe for the general help and the link!

jagge123
  • 263
  • 1
  • 5
  • 15