I have some legacy code I want to start unit-testing. It's a class like this:
export class Controller {
private something: any;
constructor() { this.something = true; }
public getSomething(): any { return this.something; }
}
Trying to unit-test it with Mocha like this:
import Controller from '../../src/Controller';
describe('Controller', () => {
let subject: any;
beforeEach( () => {
subject = new Controller(); // compiler complains here
});
describe('getOptions()', () => {
it('should get something', () => {
let result: any = subject.getOptions();
if (typeof result !== 'object') {
throw new Error('Expected object but got ' + result);
}
});
});
});
Compiler complains:
[ts] Cannot use 'new' with an expression whose type lacks a call or
construct signature.
How do I get an instance of the Controller class to run tests against?
It doesn't work because you are trying to import the Controller as a default export while it isn't.
Try:
import {Controller} from '../../src/Controller';
or:
export default class Controller {
You can read more about imports in general on the MDN page here.
Related
I'm trying to test a behaviorSubject, but keep getting:
Error: <spyOnProperty> : Property myBehaviorSubject$ does not have access type get
this is how I declare my behaviorSubject in my myService:
myBehaviorSubject$ = new BehaviorSubject<Array<ObjectA>>([{name: 'name1'}]);
and my test:
describe(`myMethod`, () => {
beforeEach(() => {
myServiceMock = {
myBehaviorSubject$: new BehaviorSubject<Array<ObjectA>>(null)
};
spyOnProperty(myServiceMock, 'myBehaviorSubject$', 'get').and.returnValue(observableOf([{name: 'name1'}] as Array<ObjectA>));
});
it('should access behaviorSubject', () => {
myServiceMock.myBehaviorSubject$.subscribe( result => {
//expect;
});
});
});
The fact that you are mocking the return value of the property access implies that you are not actually using that property directly at all. Instead of what you are doing, I'd recommend creating the spy directly, like this:
beforeEach(() => {
myServiceMock = {
myBehaviorSubject$: jasmine.createSpy('myBehaviorSubject$')
.and.returnValue(observableOf([{name: 'name1'}] as Array<ObjectA>))
};
});
This has the exact same effect as above, but it doesn't create a new instance of BehaviorSubject, which doesn't seem to be used.
I am currently write some tests for my angular application. When I mock class to check if method is correctly called, I make a mock implementation. When I do that my code is underlined with red dots because my mock does not respect the true implementation of the type.
Here is an example. Here I want mock HttpLink class in order to test that create function is called. I dont mind how HttpLink object is construct. So I mocked HttpLink class and I mock create function. But visual studio code put red dot when I call the constructor because it does not respect the true HttpLink implementation :
import { Apollo } from 'apollo-angular'
jest.mock('apollo-angular', () => {
return {
Apollo: () => {
return {
create: jest.fn()
}
}
}
})
import { HttpLink } from 'apollo-angular-link-http'
jest.mock('apollo-angular-link-http', () => {
return {
HttpLink: () => {
return {
create: jest.fn()
}
}
}
})
import { GraphqlService } from './graphql.service'
describe('GraphqlService', () => {
let apollo = new Apollo()
let httpLink = new HttpLink() // <====== This is red because type checking see this class need one argument but I mocked the constructor.
let graphqlService
beforeAll(() => {
graphqlService = new GraphqlService(apollo, httpLink)
})
it('should be created', () => {
expect(graphqlService).toBeTruthy()
})
it('should create apollo client correctly', () => {
expect(apollo.create).toHaveBeenCalled()
})
})
Is there a way to desactivate type checking that visual studio code does, but only for test files ?
You can go to the preferences and then Settings
In User Settings, you have to update following :
"typescript.validate.enable": true,
to
"typescript.validate.enable": false,
Hope it will help you !!
Jest provides a way to mock functions as described in their docs
apiGetMethod = jest.fn().mockImplementation(
new Promise((resolve, reject) => {
const userID = parseInt(url.substr('/users/'.length), 10);
process.nextTick(
() => users[userID] ? resolve(users[userID]) : reject({
error: 'User with ' + userID + ' not found.',
});
);
});
);
However these mocks only seem to work when the function is called directly in a test.
describe('example test', () => {
it('uses the mocked function', () => {
apiGetMethod().then(...);
});
});
If I have a React Component defined as such how can I mock it?
import { apiGetMethod } from './api';
class Foo extends React.Component {
state = {
data: []
}
makeRequest = () => {
apiGetMethod().then(result => {
this.setState({data: result});
});
};
componentDidMount() {
this.makeRequest();
}
render() {
return (
<ul>
{ this.state.data.map((data) => <li>{data}</li>) }
</ul>
)
}
}
I have no idea how to make it so Foo component calls my mocked apiGetMethod() implementation so that I can test that it renders properly with data.
(this is a simplified, contrived example for the sake of understanding how to mock functions called inside react components)
edit: api.js file for clarity
// api.js
import 'whatwg-fetch';
export function apiGetMethod() {
return fetch(url, {...});
}
You have to mock the ./api module like this and import it so you can set the implemenation of the mock
import { apiGetMethod } from './api'
jest.mock('./api', () => ({ apiGetMethod: jest.fn() }))
in your test can set how the mock should work using mockImplementation:
apiGetMethod.mockImplementation(() => Promise.resolve('test1234'))
In case the jest.mock method from #Andreas's answer did not work for you. you could try the following in your test file.
const api = require('./api');
api.apiGetMethod = jest.fn(/* Add custom implementation here.*/);
This should execute your mocked version of the apiGetMethod inside you Foo component.
Here is an updated solution for anyone struggling with this in '21. This solution uses Typescript, so be aware of that. For regular JS just take out the type calls wherever you see them.
You import the function inside your test at the top
import functionToMock from '../api'
Then you indeed mock the call to the folder outside of the tests, to indicate that anything being called from this folder should and will be mocked
[imports are up here]
jest.mock('../api');
[tests are down here]
Next we mock the actual function we're importing. Personally I did this inside the test, but I assume it works just as well outside the test or inside a beforeEach
(functionToMock as jest.Mock).mockResolvedValue(data_that_is_returned);
Now here's the kicker and where everyone seems to get stuck. So far this is correct, but we are missing one important bit when mocking functions inside a component: act. You can read more on it here but essentially we want to wrap our render inside this act. React testing library has it's own version of act. It is also asynchronous, so you have to make sure your test is async and also define the destructured variables from render outside of it.
In the end your test file should look something like this:
import { render, act } from '#testing-library/react';
import UserGrid from '../components/Users/UserGrid';
import { data2 } from '../__fixtures__/data';
import functionToMock from '../api';
jest.mock('../api');
describe("Test Suite", () => {
it('Renders', async () => {
(functionToMock as jest.Mock).mockResolvedValue(data2);
let getAllByTestId: any;
let getByTestId: any;
await act(async () => {
({ getByTestId, getAllByTestId } = render(<UserGrid />));
});
const container = getByTestId('grid-container');
const userBoxes = getAllByTestId('user-box');
});
});
Another solution to mock this would be:
window['getData'] = jest.fn();
Given a simple component that subscribes to the activated route query params in ngOnInit:
export class FooComponent implements OnInit {
private queryParams: any;
constructor(
private activatedRoute: ActivatedRoute
) { }
ngOnInit() {
this.activatedRoute.queryParams.subscribe(params => this.queryParams = params);
}
active(foo: number): boolean {
return this.queryParams['foo'] &&
foo === +this.queryParams['foo'];
}
}
The active function should return true when the foo query param is present and its value matches the supplied parameter.
In the accompanying unit tests for this component, I want to change the value of the query params within each it block to test the query param not being present, matching the parameter and not matching the parameter.
describe('FooComponent', () => {
let component: FooComponent;
let fixture: ComponentFixture<FooComponent>;
let activatedRoute: ActivatedRoute;
class MockActivatedRoute {
queryParams = Observable.of({});
}
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [FooComponent],
providers: [
{ provide: ActivatedRoute, useClass: MockActivatedRoute }
]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(FooComponent);
component = fixture.componentInstance;
fixture.detectChanges();
activatedRoute = fixture.debugElement.injector.get(ActivatedRoute);
});
describe('active', () => {
it('should return false if the foo query param is not present', () => {
activatedRoute.queryParams = Observable.of({});
let result = component.active(100);
expect(result).toBe(false);
});
it('should return false if the foo query param does not match the supplied parameter', () => {
activatedRoute.queryParams = Observable.of({ foo: '500' });
let result = component.active(100);
expect(result).toBe(false);
});
it('should return true if the foo query param does not match the supplied parameter', () => {
activatedRoute.queryParams = Observable.of({ foo: '500' });
let result = component.active(500);
expect(result).toBe(true);
});
});
});
Rather the value of the private queryParams member of the FooComponent class does not update within each it block. I've tried the various methods of async, fixture.whenStable(), and fakeAsync/tick.
How do I update the value of the subscription for each unit test?
It's because you are assigning a new Observable, but the client is already subscribed to the first Observable. This happens because ngOnInit is called when you first call fixture.detectChanges(). If you waited to called fixture.detectChanges() after you assign the new Observable to the queryParams, then that Observable would be used.
Another option (maybe preferred) is to instead of using an Observable, you can use a Subject. With this, you can control when data is emitted, and what to emit.
import { Subject } from 'rxjs/Subject'
import { fakeAsync, tick } from
class MockActivatedRoute {
queryParams = new Subject<any>();
}
let route: MockActivatedRoute;
beforeEach(() => {
/* configure */
route = <MockActivatedRoute>TestBed.get(ActivatedRoute);
})
it('', fakeAsync(() => {
route.queryParams.next(newparams); // emit something
tick(); // wait for resolution
fixture.detectChanges(); // detect changes (for ui)
expect(...)
}))
I say this options might be preferred as it allows for emitting multiple values in the same test.
This might be an Ionic 2 only question, as I don't see NavParams in the Angular 2 docs, but some concepts might translate so I tagged both.
Given that I call navparams.get('somekey') in order to listen to parameters that are passed in, it's tricky to mock the NavParams in tests.
For example, here's how I currently do it:
export class NavParamsMock {
public get(key): any {
return String(key) + 'Output';
}
}
This works for really basic tests, but what if I had a component that I have to test that it gets a specific type of Object, eg a User.
Then, I can do something like
export class NavParamsMock {
public get(key): any {
if (key === 'user') {
return new User({'name':'Bob'})
}
return String(key) + 'Output';
}
}
But, this doesn't work if you want to use the get(user) in another test, or even another component's spec. Say you use NavParams in 2 different components, and they both expect different result when you do get(user), it becomes increasingly tricky to mock.
Has anyone found a solution to this scenario?
You can get value of your choice by implementing your own setter method.
export class NavParamsMock {
static returnParam = null;
public get(key): any {
if (NavParamsMock.returnParam) {
return NavParamsMock.returnParam
}
return 'default';
}
static setParams(value){
NavParamsMock.returnParam = value;
}
}
Then in each test you can access the service and set your own params object.
beforeEach(() => {
NavParamsMock.setParams(ownParams); //set your own params here
TestBed.configureTestingModule({
providers: [
{provide: NavParams, useClass: NavParamsMock},
]
});
})
Rather than mocking out the class, it's easiest to just create an instance of the NavParams class, then use it. NavParams makes the data property publicly assignable, so it can be modified in each test as needed.
The below example assumes your page looks something like this:
#IonicPage()
#Component({...})
export class YourPage {
private data: string;
constructor(navParams: NavParams) {
this.data = navParams.get('data');
}
}
I.e., you call navParams.get() in your page constructor, ionViewDidLoad(), ngOnInit(), or similar initializer function. In this case, to modify the NavParams data and ensure it's used properly, you need to modify your test injected navParams.data property, then regenerate your page:
import {IonicModule, NavParams} from 'ionic-angular';
import {ComponentFixture, TestBed} from '#angular/core/testing';
describe('YourPage', () => {
let fixture: ComponentFixture<YourPage>;
let component: YourPage;
const data = {data: 'foo'};
const navParams = new NavParams(data);
function generateFixture() {
fixture = TestBed.createComponent(YourPage);
component = fixture.componentInstance;
fixture.detectChanges();
}
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [YourPage],
imports: [
IonicModule.forRoot(YourPage),
],
providers: [
{provide: NavParams, useValue: navParams},
]
});
generateFixture();
});
describe('NavParams', () => {
it('should use injected data', () => {
expect(component['data']).toEqual('foo');
});
it('should use new injected data', () => {
const newData = {data: 'bar'};
navParams.data = newData;
generateFixture();
expect(component['data']).toEqual('bar');
});
});
});
If your page calls navParams.get('key') everywhere instead of assigning to a private member, then simply reassigning the navParams.data property is sufficient in each test (no need to call generateFixture() each time).
I modified #raj's answer with my own variation of this technique. #raj's only allow you to set one parameter. Mine allows for key value storage with multiple parameters.
export class NavParamsMock {
static returnParams: any = {};
public get(key): any {
if (NavParamsMock.returnParams[key]) {
return NavParamsMock.returnParams[key];
}
return 'No Params of ' + key + ' was supplied. Use NavParamsMock.setParams('+ key + ',value) to set it.';
}
static setParams(key,value){
NavParamsMock.returnParams[key] = value;
}
}
Here is an example with multiple params
NavParamsMock
export class NavParamsMock {
static returnParams: any = {}
public get (key): any {
if (NavParamsMock.returnParams[key]) {
return NavParamsMock.returnParams[key]
}
}
static setParams (key, value): any {
NavParamsMock.returnParams[key] = value
}
}
Add to TestBed providers the following
{provide: NavParams, useClass: NavParamsMock}
Unit test
it('i am a unit test', () => {
const navParams = fixture.debugElement.injector.get(NavParams)
navParams.get =
jasmine
.createSpy('get')
.and
.callFake((param) => {
const params = {
'param1': 'value',
'param2': 'value'
}
return params[param]
})
comp.ionViewDidLoad()
})