Reputation: 14932
My code includes an if
block like this
Service:
import { isDevMode } from '@angular/core';
export class MyService {
constructor() {}
methodToTest(): {
if (!isDevMode()) {
doSomething();
} else {
doSomethingDifferentInDevMode();
}
}
}
my-service.spec.ts (the relevant lines only):
it('should run doSomething() when isDevMode() returns false', () => {
// what should i do here to mock a false value return for isDevMode() ?
expect(doSomething).toHaveBeenCalled();
});
How do i spyOn the isDevMode()
to make it return false for this single Angular unit test so that i may test that doSomething()
is called?
Upvotes: 7
Views: 3333
Reputation: 20609
Injecting a service as suggested by the accepted answer isn't always a possibility. Here is a safe (as possible) approach.
The primary issue here is that there's no disableProdMode, so if you use enableProdMode one time then all future tests will have "prod" turned on.
Fortunately "prod" mode is just derived from a global variable, ngDevMode which is an object or false. You can see its role in enableProdMode here. You just have to make sure to restore it as soon as you're done with it as there is the potential for nasty side effects.
declare let ngDevMode: object | false;
if (typeof ngDevMode === 'undefined' || !ngDevMode) {
throw new Error('These tests must be launched in dev mode.');
}
const originalDevMode = ngDevMode;
describe('my prod tests', () => {
beforeEach(() => ngDevMode = false);
afterEach(() => ngDevMode = originalDevMode);
/* tests... */
});
In the example above, ngDevMode is being set and restored before and after each test. This works in my cases, but I've seen examples where this can still have undesirable side effects. So you might actually have to set and unset it before and after a specific line.
Upvotes: 0
Reputation: 3588
The approach that I'm using is the following
import { enableProdMode, isDevMode } from '@angular/core';
declare const global: any;
describe('teststing IsDevMode', () => {
beforeEach(() => {
/**
* We must reset the devmode to true before each test, as it is the default non prod state
*
*/
global['ngDevMode'] = true;
});
it('should be true in tests', () => {
expect(isDevMode()).toBe(true);
});
it('should be false for prod using ngInternals', () => {
enableProdMode();
expect(isDevMode()).toBe(false);
});
it('should be false for prod', () => {
global['ngDevMode'] = false;
expect(isDevMode()).toBe(false);
});
});
So basically we are overwriting the global variable used for checking if the project is in devMode. You can find out how the internals work here: AngularGitHub
Upvotes: 0
Reputation: 692181
Start by wrapping this function into a service:
import { isDevMode, Injectable } from '@angular/core';
@Injectable({ providedIn: 'root' })
export class DevModeService {
constructor() {}
isDevMode() {
return isDevMode();
}
}
Then you can inject this service wherever you want, as you would do with any other service:
import { DevModeService } from 'dev-mode.service';
export class MyService {
constructor(private devModeService: DevModeService) {}
methodToTest(): {
if (!this.devModeService.isDevMode()) {
doSomething();
} else {
doSomethingElse();
}
}
}
And you can then spy on the DevModeService when testing MyService, like you would do with any other dependency:
it('should do something when in dev mode') {
const devModeService = TestBed.inject(DevModeService);
spyOn(devModeService, 'isDevMode').and.returnValue(true);
const myService = TestBed.inject(MyService);
myService.methodToTest();
// ...
}
it('should do something else when in prod mode') {
const devModeService = TestBed.inject(DevModeService);
spyOn(devModeService, 'isDevMode').and.returnValue(false);
const myService = TestBed.inject(MyService);
myService.methodToTest();
// ...
}
Upvotes: 12
Reputation: 13205
Looks like the answer is in the comments. Thanks @JBNizet.
// dev-mode-service.ts
import { isDevMode } from '@angular/core';
export class DevModeService {
constructor() {}
isDevMode(): {
return isDevMode();
}
}
// MyService.ts
import { isDevMode } from 'dev-mode-service';
export class MyService {
constructor() {}
methodToTest(): {
if (!isDevMode()) {
doSomething();
} else {
doSomethingDifferentInDevMode();
}
}
}
Upvotes: 0