I am facing an issue when calling mock service inside beforeEach function.
To make get the access token from the login so i need to run this mock service for all the function in the spec file so that we can make it run this in an orderly manner.
beforeEach(inject([LoginService, MockBackend], (Service: LoginService, mockBackend: MockBackend) => {
loginService = Service;
backend = mockBackend;
it('#login should call endpoint and return it\'s result', (done) => {
backend.connections.subscribe((connection: MockConnection) => {
const options = new ResponseOptions({
body: JSON.stringify({ success: true })
});
connection.mockRespond(new Response(options));
// Check the request headers
expect(connection.request.headers.get('Content-Type')).toEqual('application/json');
});
loginService.login('new', 'secret')
.subscribe((response) => {
sessionStorage.setItem('token', JSON.stringify(response.token));
done();
fixture = TestBed.createComponent(DashboardComponent);
router.initialNavigation();
component = fixture.componentInstance;
fixture.detectChanges();
},
(error) => {
expect(error).toThrowError();
});
});
}));
I am trying to perform unit test on vuex actions, using Mocha and Sinon
here is my action.spec.js
import actions from '#/vuex/actions'
import * as types from '#/vuex/mutation_types'
describe('actions.js', () => {
var server, store, lists, successPut, successPost, successDelete
successDelete = {'delete': true}
successPost = {'post': true}
successPut = {'put': true}
beforeEach(() => {
// mock shopping lists
lists = [{
id: '1',
title: 'Groceries'
}, {
id: '2',
title: 'Clothes'
}]
// mock store commit and dispatch methods
store = {
commit: (method, data) => {},
dispatch: () => {
return Promise.resolve()
},
state: {
shoppinglists: lists
}
}
sinon.stub(store, 'commit')
// mock server
server = sinon.fakeServer.create()
server.respondWith('GET', /shoppinglists/, xhr => {
xhr.respond(200, {'Content-Type': 'application/json'}, JSON.stringify(lists))
})
server.respondWith('POST', /shoppinglists/, xhr => {
xhr.respond(200, {'Content-Type': 'application/json'}, JSON.stringify(successPost))
})
server.respondWith('PUT', /shoppinglists/, xhr => {
xhr.respond(200, {'Content-Type': 'application/json'}, JSON.stringify(successPut))
})
server.respondWith('DELETE', /shoppinglists/, xhr => {
xhr.respond(200, {'Content-Type': 'application/json'}, JSON.stringify(successDelete))
})
server.autoRespond = true
})
afterEach(() => {
// restore stubs and server mock
store.commit.restore()
server.restore()
})
describe('populateShoppingLists', () => {
it('should call commit method with POPULATE_SHOPPING_LIST string parameter', done => {
actions.populateShoppingLists(store).then(() => {
expect(store.commit).to.have.been.calledWith(types.POPULATE_SHOPPING_LISTS, lists)
done()
}).catch(done)
})
})
describe('changeTitle', () => {
it('should call commit method with CHANGE_TITLE string', (done) => {
let title = 'new title'
actions.changeTitle(store, {title: title, id: '1'}).then(() => {
expect(store.commit).to.have.been.calledWith(types.CHANGE_TITLE, {title: title, id: '1'})
done()
}).catch(done)
})
})
describe('updateList', () => {
it('should return successful PUT response', (done) => {
actions.updateList(store, '1').then((data) => {
expect(data.data).to.eql(successPut)
done()
}).catch(done)
})
})
describe('createShoppingList', () => {
it('should return successful POST response', (done) => {
let newList = { title: 'new list', id: '3' }
actions.createShoppingList(store, newList).then((testResponse) => {
console.log('testResponse: ', testResponse)
expect(testResponse.body).to.eql(successPost)
done()
}).catch(done)
})
})
})
here is my action.js
import { CHANGE_TITLE, POPULATE_SHOPPING_LISTS } from './mutation_types'
import api from '../api'
import getters from './getters'
export default {
populateShoppingLists: ({ commit }) => {
return api.fetchShoppingLists().then(response => {
commit(POPULATE_SHOPPING_LISTS, response.data)
})
},
changeTitle: (store, data) => {
store.commit(CHANGE_TITLE, data)
return store.dispatch('updateList', data.id)
},
updateList: (store, id) => {
let shoppingList = getters.getListById(store.state, id)
return api.updateShoppingList(shoppingList)
},
createShoppingList: (store, shoppinglist) => {
return api.addNewShoppingList(shoppinglist).then((actionResponse) => {
console.log('actionResponse: ', actionResponse)
store.dispatch('populateShoppingLists')
})
},
}
running my unit tests , I have an issue with the createShoppingList test
console.log
actions.js
populateShoppingLists
✓ should call commit method with POPULATE_SHOPPING_LIST string parameter
changeTitle
✓ should call commit method with CHANGE_TITLE string
updateList
✓ should return successful PUT response
LOG LOG: 'actionResponse: ', Response{url: 'http://localhost:3000/shoppinglists', ok: true, status: 200, statusText: 'OK', headers: Headers{map: Object{Content-Type: ...}}, body: Object{post: true}, bodyText: '{"post":true}'}
LOG LOG: 'testResponse: ', undefined
createShoppingList
✗ should return successful POST response
undefined is not an object (evaluating 'testResponse.body')
webpack:///test/unit/specs/vuex/actions.spec.js:90:28 <- index.js:15508:28
webpack:///~/vue-resource/dist/vue-resource.es2015.js:151:0 <- index.js:17984:52
webpack:///~/vue/dist/vue.esm.js:701:0 <- index.js:3198:18
nextTickHandler#webpack:///~/vue/dist/vue.esm.js:648:0 <- index.js:3145:16
whicj indicates that in the createShoppingList action, the reponse is not sent back on the return, so expect(testResponse.body).to.eql(successPost) is not true...
what's wrong with my Promise handling in this case ?
thanks for feedback
You're on the right track - testResponse is undefined, because createShoppingList resolves with the return value of addNewShoppingList.then, which is unspecified, and defaults to undefined.
Should createShoppingList resolve with addNewShoppingList's response or the response from populateShoppingLists? If the former, return actionResponse from the handler:
return api.addNewShoppingList(shoppinglist).then((actionResponse) => {
store.dispatch('populateShoppingLists')
return actionResponse
});
As a side-note, because the actions you're testing are promises, you can get rid of done in your tests by returning the actions directly:
it('should call commit method with POPULATE_SHOPPING_LIST string parameter', () => {
// mocha will fail the test if the promise chain rejects or the expectation is not met
return actions.populateShoppingLists(store).then(() => {
expect(store.commit).to.have.been.calledWith(types.POPULATE_SHOPPING_LISTS, lists)
})
})
I am struggling to unit test Angular2 service which has async Http call in the constructor (now I wonder should it be here in the first place).
Example code below - the mocked call never seems to have been executed and I am not sure where should I put it in. The test fails as the property I am asserting is undefined at the time of execution. I tried with fakeAsync and tick() but that didnt work neither.
Service class:
#Injectable
export class Service {
private data: any; //some object that will be returned from server
constructor(private http: Http) {
this.http.get('url')
.map( (res: Response) => res.json())
.subscribe(res => this.data = res);
}
getId() {
return data.id;
}
}
The unit test:
describe('service test...', () => {
let service: Service;
let backend: MockBackend;
let result = { id: 123 };
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpModule],
providers: [
Service,
{
provide: Http,
useFactory: (mockBackend, options) => {
return new Http(mockBackend, options);
},
deps: [MockBackend, BaseRequestOptions]
},
MockBackend,
BaseRequestOptions
]
});
});
beforeEach(inject([Service, MockBackend], (s, mb) => {
service = s;
backend = mb;
backend.connections.subscribe((conn) => {
conn.mockRespond(new Response(new ResponseOptions({body: result})));
});
}));
describe('test...', () => {
it('should have id of 123...', async(() => {
expect(service.getId()).toEqual(123);
}));
});
});
I have a DataService and I want to assert that the year is getting set in the query string correctly. Is there a way to spyOn the http.get call or to access it? I don't know the correct approach to testing this. I'm using Angular 2.2.0.
The DataService
constructor(private http: Http) { }
public getEnergyData(option: string): Promise<EnergyDataDto[]> {
return this.http.get(this.getEnergyDataApiUrl(option)).toPromise().then((response) => {
this.energyDataCache = this.parseEnergyDataResponse(response);
return this.energyDataCache;
}).catch(this.handleError);
}
protected getEnergyDataApiUrl(option: string) {
return `/api/solar?year=${option}`;
}
protected parseEnergyDataResponse(response: Response) {
return response.json().data;
}
dataservice.spec.ts
describe('Given the DataService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpModule],
providers: [DataService, { provide: XHRBackend, useClass: MockBackend }],
});
});
describe('When getting the energy data', () => {
let backend: MockBackend;
let service: EnergyDataService;
let fakeEnergyData: EnergyDataDto[];
let response: Response;
const makeEnergyData = () => {
let data = [];
let one = new EnergyDataDto();
one.year = 2007;
one.countryName = 'Denmark';
one.quantity = '100000';
data.push(one);
return data;
};
beforeEach(inject([Http, XHRBackend], (http: Http, be: MockBackend) => {
backend = be;
service = new EnergyDataService(http);
fakeEnergyData = makeEnergyData();
let options = new ResponseOptions({ status: 200, body: { data: fakeEnergyData } });
response = new Response(options);
}));
it('should return fake values', async(inject([], () => {
backend.connections.subscribe((c: MockConnection) => c.mockRespond(response));
service.getEnergyData('all').then(data => {
expect(data.length).toBe(1);
expect(data[0].countryName).toBe('Denmark');
});
})));
it('should use year in query string', async(inject([], () => {
spyOn(service, 'getEnergyDataApiUrl').and.callThrough();
backend.connections.subscribe((c: MockConnection) => c.mockRespond(response));
service.getEnergyData('2007').then(data => {
// I was hoping to use backendend somehow instead, but it's not in scope when I debug it.
expect((<any>service).getEnergyDataApiUrl).toHaveBeenCalledWith('/api/solar?year=2007');
});
})));
You should do this in the mockBackend.connections subscription. This is when you have access to the URL from the MockConnection
backend.connections.subscribe((c: MockConnection) => {
expect(c.request.url).toBe(...)
c.mockRespond(response)
});
I'm trying to mock an authentication service. I post my login credentials and get a token back, i then save the token.
Basically the test runs if i comment the line:
this.authenticationService.saveToken(res.json().token);
I injected the service in the test, and this line doesn't affect the output.
My error is "Cannot read property 'saveToken' of undefined thrown"
Here is my service:
private authSuccess(res: Response){
this.isAuthenticated = true;
this.authenticationService.saveToken(res.json().token);
return res.json();
}
public postLogin(loginData: LoginModel): Observable<any>{
let body = JSON.stringify(loginData);
var headers = new Headers();
headers.append('Content-Type', 'application/json');
let options = new RequestOptions({ headers: headers });
return this.http.post(this.loginUrl, body, options)
//success
.map(this.authSuccess)
//error
.catch(this.handleError);
}
Here is my test:
describe('login service tests', () => {
let loginService: LoginService;
let backend: MockBackend;
let injector: Injector;
let authenticationService: AuthenticationService;
beforeEach(() => {
injector = ReflectiveInjector.resolveAndCreate(<any> [
LoginService,
AuthenticationService,
BaseRequestOptions,
MockBackend,
provide(Http, {
useFactory: (mockBackend, defaultOptions) => new Http(mockBackend, defaultOptions),
deps: [MockBackend, BaseRequestOptions]
})
]);
loginService = <LoginService> injector.get(LoginService);
backend = <MockBackend> injector.get(MockBackend);
authenticationService = <AuthenticationService> injector.get(AuthenticationService)
});
afterEach(() => backend.verifyNoPendingRequests());
it('should authenticate with the web api', () => {
let loginUrl = Constants.loginUrl;
let loginData:LoginModel = new LoginModel('username', 'password');
backend.connections.subscribe((c: MockConnection) => {
expect(c.request.url).toEqual(loginUrl);
c.mockRespond(new Response(new ResponseOptions({ body: '{"token": "mockAuth"}' })));
});
//Correct login data
loginService.postLogin(loginData).subscribe((data) => {
expect(data.token).toBe('mockAuth');
});
});
Also, how do you guys debug when running tests? console.log doesn't seem to work and neither does debugger;
Alright, so it seems the issue was calling the method in the .map section instead of executing everything directly there.
Solution:
delete authsuccess
.map((response) => {
this.isAuthenticated = true;
this.authenticationService.saveToken(response.json().token);
return response.json();
})