AngularJS testing with Jest - javascript

I am trying to mock HTTP requests for AngularJS in Jest.
I have a service like this
public UserService(user) {
let url = this.env.getUserEndpoint("/users");
let deferred = this.$q.defer<any>();
this.$http.post<{ user: IUser }>(url,user).then( (result) => {
deferred.resolve(result.data.user);
}, (error) => {
deferred.reject(this.httpErrorHandlingService.handleHttpError(error, "creating new user"));
});
return deferred.promise;
}
Test:
beforeEach(angular.mock.module("test"));
var userService;
beforeEach(angular.mock.inject(function (UserService) {
userService = UserService;
}));
it('should create new user', function () {
const newUser = {
name: "testUser",
password: "12345"
};
return userService.createNewUser(newUser).then(user => expect(user.name).toEqual("testUser")
});
The error I get is Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.Timeout
What am I doing wrong, and how can I mock AngularJS HTTP requests the easiest way?

You don't need to create your own promise - $http.post() returns one for you. Then you can return the response data in your success and error function callbacks in your then method, and these will be handed to the promise returned by $http.post(). This should simplify your service code a bit:
public UserService(user) {
public createNewUser(user) {
let url = this.env.getUserEndpoint("/users");
return this.$http.post<{ user: IUser }>(url, user).then((result) => {
return result.data.user;
}, (error) => {
this.httpErrorHandlingService.handleHttpError(error, "creating new user");
});
}
}

Related

How to test a function which has calls a another API function in it - NodeJS

I have a function, which has another function in it. And in the second function we are making an API call. So how can I write a unit test for this scenario? I don't want to make an actual API call, I want to mock it.
const getData = async (data) => {
const res = await got.post(url,{
json: {data}
});
const data = res.data;
return data;
}
function firstFunction(args) {
// perform some operation with args and it's stored in variable output.
let output = args;
let oo = getData(args);
console.log(oo)
}
When running unit test you don't have to call real API calls. You have to encapsulate your component and provide any external information.
With jest you can mock the http call and return what you want. And also you can check if the mock has been called.
import { got } from "anyplace/got";
import { firstFunction } from "anyplace2";
jest.mock("anyplace/got", () => ({
// here you provide a mock to any file that imports got to make http calls
got: {
// "mockResolvedValue" tells jest to return a promise resolved
// with the value provided inside. In this case {data: 'what you
// want here'}
post: jest.fn().mockResolvedValue({data: 'what you want here'});
}
}));
describe('My test', () => {
beforeEach(() => {
// This will clear all calls to your mocks. So for every test you will
// have your mocks reset to zero calls
jest.clearAllMocks();
});
it('Should call the API call successfully', () => {
// execute the real method
firstFunction({arg: 1});
// check that the API has been called 1 time
expect(got.post).toHaveBeenCalledTimes(1);
expect(got.post).toHaveBeenCalledwith("myurlhere", {data: {arg: 1}});
})
});
You can simulate it with setTimeout, I further provided a mock response so after 1000ms it will send a Promise with this user array
const getData = () => {
return new Promise((resolve, reject) => {
setTimeout(resolve({
users: [
{ name: "Michael" },
{ name: "Sarah" },
{ name: "Bill" },
]
}), 1000)
})
}

Soap call with NestJS

I have to call SOAP webservice and serve it. I am using strong-soap library(https://github.com/loopbackio/strong-soap). I get my data in the service and I can see it in console.log() but I can't send this data to my controller to serve it. I have tried using pipe(map()) and I have looked into this following topics
(https://www.freecodecamp.org/news/an-express-service-for-parallel-soap-invocation-in-under-25-lines-of-code-b7eac725702e/)
but no luck. I either get 'can't subscribe to undefined' or my request is passing without my controller getting the data and serving it.
Here is my controller.ts
export class MyController {
constructor(private readonly service: Service){}
#Get('/example')
getAll(#Query() query: Query){
return this.service.getAll(query);
}
}
and here is my service
export class Service {
private URL = process.env.URL;
constructor() { }
async getAll(query: OrderRequest) {
const request = {
req: {
req: query,
}
};
return await soap.createClient(this.URL, (err, client) => {
if (err) {
console.error(err);
} else {
let method = client.myMethodName;
method(request, (err, result, envelope, soapHeader) => {
// here I am getting the data but 'return result. is not sending it to the controller
console.log(result[0])
return result
});
}
});
}
}
As I said I have tried with map and pipe like this:
return await soap.createClient(this.URL).pipe(map((err, client) => {
if (err) {
console.error(err);
} else {
let method = client.myMethodName; // here I have an error
method(request, (err, result, envelope, soapHeader) => {
console.log(result)
});
}
}))
but I can't pass my method in the client.
Thanks
OK I have found the solution. Thing is the function above was returning the promise so solution was to initialize new promise and handle reject and resolve. Like this we get the results in resolve and take them with .then() in our controller.
Here is the code:
service.ts
getAll(query: Query) {
const request = {query: {query: query}};
return new Promise ((resolve, reject) => { // here we add new Promise
soap.createClient(this.URL, (err, client) => {
if (err) {
return reject(err); // if error reject promise
} else {
return resolve(client); // else resolve and send client
});
}
});
}
)}
controller.ts
getAll(#Query() query: query){
console.log(this.service.getAll(query))
return this.service.getAll(query).then(result=>result)
}

Jest testing mongoose model instantiation

I'm trying to test a REST API built with express and mongoose, I'm using jest and supertest for the http calls; also I'm relatively new to testing with javascript.
When testing a creation url I wan't to make sure the instantiation is called using just the req.body object but I'm not sure how to do it, after reading a lot about differences between mock objects and stubs and some of the Jest documentation my last try looks like this:
test('Should instantiate the model using req.body', done => {
const postMock = jest.fn();
const testPost = {
name: 'Test post',
content: 'Hello'
};
postMock.bind(Post); // <- Post is my model
// I mock the save function so it doesn't use the db at all
Post.prototype.save = jest.fn(cb => cb(null, testPost));
// Supertest call
request(app).post('/posts/')
.send(testPost)
.then(() => {
expect(postMock.mock.calls[0][0]).toEqual(testPost);
done();
})
.catch(err => {throw err});
});
Also I would like to know how to manually fail the test on the promise rejection, so it doesn't throws the Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
As it stands, you're performing more of a integration test rather than isolating the route handler function itself and testing just that.
First I would break away the handler for /posts/ to its own file (assuming you haven't done this already):
controllers/post-controller.js
const Post = require('./path/to/models/post')
exports.store = async (req, res) => {
const post = await new Post(req.body).save()
res.json({ data: post }
}
Next simply use the handler wherever you defined your routes:
const express = require('express')
const app = express()
const postController = require('./path/to/controllers/post-controller')
app.post('/posts', postController.store)
With this abstraction we can now isolate our postController.store and test that it works with req.body. Now since we need to mock mongoose to avoid hitting an actual database, you can create a mocked Post like so (using the code you already have):
path/to/models/__mocks__/post.js
const post = require('../post')
const mockedPost = jest.fn()
mockedPost.bind(Post)
const testPost = {
name: 'Test post',
content: 'Hello'
}
Post.prototype.save = jest.fn(cb => {
if (typeof cb === 'function') {
if (process.env.FORCE_FAIL === 'true') {
process.nextTick(cb(new Error(), null))
} else {
process.nextTick(cb(null, testPost))
}
} else {
return new Promise((resolve, reject) => {
if (process.env.FORCE_FAIL === 'true') {
reject(new Error())
} else {
resolve(testPost)
}
})
}
})
module.exports = mockedPost
Notice the check for process.env.FORCE_FAIL if for whatever reason you wanted to fail it.
Now we're ready to test that using the req.body works:
post-controller.test.js
// Loads anything contained in `models/__mocks__` folder
jest.mock('../location/to/models')
const postController = require('../location/to/controllers/post-controller')
describe('controllers.Post', () => {
/**
* Mocked Express Request object.
*/
let req
/**
* Mocked Express Response object.
*/
let res
beforeEach(() => {
req = {
body: {}
}
res = {
data: null,
json(payload) {
this.data = JSON.stringify(payload)
}
}
})
describe('.store()', () => {
test('should create a new post', async () => {
req.body = { ... }
await postController(req, res)
expect(res.data).toBeDefined()
...
})
test('fails creating a post', () => {
process.env.FORCE_FAIL = true
req.body = { ... }
try {
await postController.store(req, res)
} catch (error) {
expect(res.data).not.toBeDefined()
...
}
})
})
})
This code is untested, but I hope it helps in your testing.

Angular2 change service method from callback to Async

I started a simple Angular2 Electron app, and I have a service method querying a local SQL Server database. Everything works fine so far. Now I am trying to get the results of the service DB call to my component and display it somehow.
The problem is that the query logic is written more for callback syntax:
sql.query(sqlString, (err, result) => {
...
callback(result);
...
});
I'm having a hard time rewriting it to return a promise, since the result will always be within result parameter of the query command function. My component looks like this:
export class LinkDocRetriever {
constructor(private myService: MyService) { }
results = "";
loadMyData(id: number): void {
let tcData = this.myService.getMyData();
tcData.forEach(element => {
this.results += element.FileName + " " + "....\n";
});
};
}
And my service looks like this:
import { Injectable } from "#angular/core";
import * as sql from "mssql";
#Injectable()
export class MyService {
getMyData():Array<MyItem> {
let myData:Array<MyItem> = [];
let config = {
user: "sa",
password: "xxx",
server: "localhost",
database: "mydb"
};
const pool1 = new sql.ConnectionPool(config, err => {
if (err) {
console.log("connect erro: " + err);
}
let q:string = `SELECT TOP 10 * FROM MyTable;`;
let final = pool1.request()
.query<MyItem>(q, (err, result) => {
if (err) {
console.log("request err: " + err);
}
console.log("db result count: " + result.recordsets[0].length);
result.recordsets[0].forEach(row => {
myData.push(row);
});
});
});
return myData;
}
}
I do get a result back, but the component never sees it since it comes back before the results are returned.
I've tried doing an await on the query call, within the ConnectionPool function, but I get an error stating that await can only be called within an async function, even though I have async set on that method. The mssql package has an Async/ Await section, but the given syntax on that page gives errors, when I try it.
Any idea how I can write this using a promise?
As you pointed out, there are 3 way to handle async functions: using callback, using promise, and using Async/ Await. I will try to show all three ways but you should learn about event loop in javascript and how it takes care of async functions.
Callback
Callback is technically fastest way to handle async functions but it is quite confusing at first and might create something called callback hell if not used properly. Callback hell is very terrible that someone even created a website for it http://callbackhell.com/.
So you code can be rewritten as:
export class LinkDocRetriever {
constructor(private myService: MyService) { }
results = "";
loadMyData(id: number): void {
// call getMyData with a function as argument. Typically, the function takes error as the first argument
this.myService.getMyData(function (error, tcData) {
if (error) {
// Do something
}
tcData.forEach(element => {
this.results += element.FileName + " " + "....\n";
});
});
};
}
Service
import { Injectable } from "#angular/core";
import * as sql from "mssql";
#Injectable()
export class MyService {
// Now getMyData takes a callback as an argument and returns nothing
getMyData(cb) {
let myData = [];
let config = {
user: "sa",
password: "xxx",
server: "localhost",
database: "mydb"
};
const pool1 = new sql.ConnectionPool(function(config, err) {
if (err) {
// Error occured, evoke callback
return cb(error);
}
let q:string = `SELECT TOP 10 * FROM MyTable;`;
let final = pool1.request()
.query<MyItem>(q, (err, result) => {
if (err) {
console.log("request err: " + err);
// Error occured, evoke callback
return cb(error);
}
console.log("db result count: " + result.recordsets[0].length);
result.recordsets[0].forEach(row => {
myData.push(row);
});
// Call the callback, no error occured no undefined comes first, then myData
cb(undefined, myData);
});
});
}
}
Promise
Promise is a special object that allows you to control async function and avoid callback hell because you won't have to use nested callback but only use one level then and catch function. Read more about Promise here
Component
export class LinkDocRetriever {
constructor(private myService: MyService) { }
results = "";
loadMyData(id: number): void {
this.myService.getMyData()
.then((tcData) => {
// Promise uses then function to control flow
tcData.forEach((element) => {
this.results += element.FileName + " " + "....\n";
});
})
.catch((error) => {
// Handle error here
});
};
}
Service
#Injectable()
export class MyService {
// Now getMyData doesn't take any argument at all and return a Promise
getMyData() {
let myData = [];
let config = {
user: "sa",
password: "xxx",
server: "localhost",
database: "mydb"
};
// This is what getMyData returns
return new Promise(function (resolve, reject) {
const pool1 = new sql.ConnectionPool((config, err) => {
if (err) {
// If error occurs, reject Promise
reject(err)
}
let q = `SELECT TOP 10 * FROM MyTable;`;
let final = pool1.request()
.query(q, (err, result) => {
if (err) {
// If error occurs, reject Promise
reject(err)
}
console.log("db result count: " + result.recordsets[0].length);
result.recordsets[0].forEach((row) => {
myData.push(row);
});
//
resolve(myData);
});
});
})
}
}
Async/await
Async/await was introduced to address the confusion you was having when dealing with callbacks and promises. Read more about async/await here
Component
export class LinkDocRetriever {
constructor(private myService: MyService) { }
results = "";
// Look. loadMyData now has to have async keyword before to use await. Beware, now loadMyData will return a Promise.
async loadMyData(id) {
// By using await, syntax will look very familiar now
let tcData = await this.myService.getMyData(tcData);
tcData.forEach((element) => {
this.results += element.FileName + " " + "....\n";
});
};
}
Service would be exactly the same as in Promise because Async/await was created especially to deal with them.
NOTE: I remove some Typescript feature from your code because I am more accustomed to vanilla JS but you should be able to compile them because Typescript is a superset of JS.

Mocha Unit Testing of Controller resolving promise coming from services

I have controller :
function(req, res) {
// Use the Domain model to find all domain
CIO.find(function(err, CIOs) {
if (err) {
response = responseFormat.create(false, "Error getting CIOs", err, {});
res.status(400).json(response);
} else {
var metrics = {
"count": CIOs.length
};
// .then means it will wait for it to finish, then let you have the result
var promises = [];
for (i in CIOs) {
promises.push(Analysis.structureMetrics(CIOs[i].toObject()))
}
var output = []
var errors = []
Q.allSettled(promises)
.then(function(results) {
for (i in results) {
if (results[i].state === "rejected") {
console.log(results[i])
errors.push(results[i].reason.errors)
output.push(results[i].reason)
} else {
output.push(results[i].value)
}
}
}).then(function() {
response = responseFormat.create(true, "List of all CIOs", output, metrics, errors);
res.status(200).json(response);
})
}
});
};
and cio.test file :
describe('/cio', function() {
describe('GET', function() {
//this.timeout(30000);
before(function() {
});
it('should return response', function(done) {
var response = http_mocks.createResponse({eventEmitter: require('events').EventEmitter})
var request = http_mocks.createRequest({
method: 'GET',
url: '/cio',
})
//var data = JSON.parse( response._getData() );
response.on('end', function() {
response.statusCode.should.be.equal(400);
done();
})
cioCtrl.getCIOs(request, response);
});
});
});
getting Error
Error: timeout of 10000ms exceeded. Ensure the done() callback is being called in this test
1>I have already tried increasing the time, but It doesn't work.
2> What I found is response.('end', function(){}) is not getting called, but not sure why
Any help would be appreciated.
Thanks!
Very good approach for unit testing is using the dependency injection.
For this, your controller file should be something like this:
module.exports = class MyController {
constructor(CIO) {
this._CIO = CIO;
this.handler = this.handler.bind(this);
}
handler(req, res) {
// your code here using this._CIO
}
};
Than in your main file, you create instance of controller:
const MyController = require('./controllers/my-controller');
// require your CIO, or create instance...
const CIO = require('./CIO');
const myController = new MyController(CIO);
You simply then pass instance of controller, or it's handler function to the place where it will be used.
Using this approach allows you to test well.
Assume your 'it' will look something like this:
it('should work', function(done) {
const fakeCIO = {
find: function() {
done();
}
};
const myController = new MyController(fakeCIO);
myController.handler();
});
Basic differences between testing techniques:
unit test - you test one single unit, how it calls functions, makes assignments, returns values
integration test - you add database to your previous test and check how it is stored/deleted/updated
end-to-end test - you add API endpoint to previous integration test and check how whole your flow works
Update:
Using async/await approach you will be able to test more things using your controller. Assume modifying it in something like this:
async function(req, res) {
try {
const CIOs = await CIO.find();
const metrics = {
"count": CIOs.length
};
const promises = CIOs.map(el => Analysis.structureMetrics(el.toObject());
for(const promise of promises) {
const result = await promise();
// do whatever you need with results
}
} catch(err) {
const response = responseFormat.create(false, "Error getting CIOs", err, {});
res.status(400).json(response);
}
Using such approach, during unit testing you can also test that your controller calls methods:
responseFormat.create
Analysis.structureMetrics
res.status
res.json
test catch branch to be executed
All this is done with fake objects.
And sure using OOP is not mandatory, it's just a matter of habits, you can do the same using functional style, with closures, for example.

Categories

Resources