Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 7 years ago.
Improve this question
I'm actually creating an application and I need to make some integration tests.
I'm actually wondering how should I do to avoid the real behaviour of my express call back routes.
Example: admitting that I want to post a data on an express route and check that I get a HTTP 200 response code but without really adding the object to my database.
EDIT: I m using supertest (https://github.com/visionmedia/supertest)
How can I do?
request
.post('/users')
.send({
message : JSON.stringify({
date: Math.round((new Date()).getTime() / 1000),
type_fiche: 'test',
id_fiche: 123
})
})
.expect(200)
.end(done);
});
EDIT 2: Is it possible to make something like a mock? Like faking a request?
You know that if I post something, it's that I need to insert something in my database right ?
I dont want to insert anything at all during myintegration tests, just test the result http response code.
I mean, admiting that I have an url like /client, and I post a new client on it. It will create a new row in the table. I dont want any row to be inserted, but I need to test the response code of the request.
Is that possible?
I would suggest adding sinon.js into the mix or any other mocking library of your choice. It will probably require quite a bit of refactoring in order to inject the mocked components.
The main idea is to mock user repository which encapsulates all db communication with user data and mock it in tests. Then you will inject mocked repository into router and test express app with supertest. Check out the example of testing imaginary user API for GET request.
var routerFactory = require('../path/to/router/factory');
describe('app router', function () {
var app, repository, router;
beforeEach(function () {
app = require('express')();
repository = createFakeUserRepository();
router = createRotuter(repository);
});
describe('getting user by id', function () {
beforeEach(function () {
app.use('/test'm router);
testedApp = supertest(app);
});
describe('GET /user/id', function (){
beforeEach('setup for valid response', function () {
repository.getById.returns(Promise.resolve({ expected: 'valid response' }))
})
it('returns 200', function (done){
testedApp.get('/test/user/id')
.expect(200, JSON.stringify({expected: 'valid response'}), done);
})
})
})
})
})
function createFakeUserRepository() {
return {
getById(): sinon:stub()
save(): sinon:stub()
};
}
function createRouter(fakeUserRepository) {
return routerFactory(fakeUserRepository);
}
You can use nock module. Its simple to use and you can get whatever data you want and you can test it.
Ex: nock('http://example.com')
.get('/users')
.query(true)
.reply({results: [{id: 'pgte'}]});
Inside supertest you can test the 'id' value. For error cases you can use replyWithError. You can get more details here: https://www.npmjs.com/package/nock
Related
I'm new to javascript, node.js (or backend at all). I am trying to create a controller for the login page requests and I am confused about getting data from the MYSQL table and User Authentication and working with JWT package !
In my Controller, I first check if the user input is available in the user table (with a simple stored procedure), then I compare the database password and the user input, after this I want to create a token and with limited time. (I have watched some tutorial videos about JWT and there is no problem with it), my main problem is to figure out how to write a proper controller with this functions?
I have 2 other questions:
1.Is it the right and secure way to get data from MySQL table inside the route? Or should I create a JS class for my controller? (I'm a bit confused and doubtful here)
2.Assuming that comparePassword() returns true, how can I continue coding outside of the db.query callback function scope? Because I have to execute comparePasssword() inside db.query callback
loginController.js :
const { validationResult } = require('express-validator');
const bcrypt = require('bcrypt');
const db = require('../../sqlConnection')
let comparePassword = (dbPass, inputPass) => {
bcrypt.compare(inputPass, dbPass, function(err, result) {
console.log(result)
});
}
// for get request
exports.getController = (req, res) => {
res.send('login')
}
// for post request
exports.postController = (req, res) => {
let errors = validationResult(req)
if(!errors.isEmpty()) {
res.status(422).json({ errors: errors.array() })
}
// find data from MYSQL table
let sql = `CALL findUser(?)`
db.query(sql, [req.body.username], (err, res) => {
if(err) console.log(err)
//console.log(Object.values(JSON.parse(JSON.stringify(res[0]))))
var data = JSON.stringify(res[0])
data = JSON.parse(data).find(x => x)
data ? comparePassword(data.password, req.body.password) : res.status(400).send('cannot find
user')
})
res.send('post login')
}
login.js :
const express = require('express')
const router = express.Router()
const { check } = require('express-validator');
const loginCont = require('../api/controllers/loginController')
router.route('/')
.get(
loginCont.getController
)
.post(
[
check('username').isLength({min: 3}).notEmpty(),
check('password').isLength({min: 4}).notEmpty()
],
loginCont.postController
)
module.exports = router
In my point of view, looks like there is no easy answer for your question so I will try to give you some directions so you can figure out which are the gaps in your code.
First question: MySQL and business logic on controller
In a design pattern like MVC or ADR (please take a look in the links for the flow details) The Controllers(MVC) Or Actions(ADR) are the entry point for the call, and a good practice is to use these entry points to basically:
Instantiate a service/class/domain-class that supports the request;
Call the necessary method/function to resolve what you want;
Send out the response;
This sample project can help you on how to structure your project following a design pattern: https://riptutorial.com/node-js/example/30554/a-simple-nodejs-application-with-mvc-and-api
Second question: db and continue the process
For authentication, I strongly suggest you to take a look on the OAuth or OAuth2 authentication flow. The OAuth(2) has a process where you generate a token and with that token you can always check in your Controllers, making the service a lot easier.
Also consider that you may need to create some external resources/services to solve if the token is right and valid, but it would facilitate your job.
This sample project should give you an example about how to scope your functions in files: https://github.com/cbroberg/node-mvc-api
Summary
You may have to think in splitting your functions into scoped domains so you can work with them in separate instead of having all the logic inside the controllers, then you will get closer to classes/services like: authenticantion, user, product, etc, that could be used and reused amount your controllers.
I hope that this answer could guide you closer to your achievements.
MEAN stack newbie here. I'm having difficulty understanding how delete works in MEAN. I'm using this SO Q&A and tutorial as guides, but whenever I test it out I get an error saying the data can't be deleted. Can somebody tell me what I've been doing wrong?
Here are my codes:
Controller JS
$scope.deleteProduct = function (value, idx) {
var this_id = value._id;
// delete
$http.delete('/api/products/delete:' + this_id)
.success(function (data) {
console.log(data);
})
.error(function (data) {
console.log('Error: ' + data);
})
}
Node Server
app.delete('/api/products/delete:', productController.delete);
Server's "Controller"
module.exports.delete = function (req, res) {
Service.remove({
_id: req.params._id
}, function (err, service) {
if (err) {
res.send(err);
}
else {
res.json({message: "Delete successful."});
}
});
}
This is how I understood this. Is this correct?
Controller JS gets the id to be deleted and calls $http's delete request(?), using said ID and the /api/products/delete:.
Node Server sees that I called '/api/products/delete:' and passes the request to Server's Controller to complete the request.
Server's Controller deletes the data and returns status.
Where did I go wrong? Please help.
Also, I've been seeing some posts that say $resource works better than $http. Why?
Thank you.
I think you've got a couple things wrong here.
In Express in order to use params you need to have something in the route that can be replaced. i.e /api/:id express replaces the :id with whatever you pass in so if you send /api/1, request.params.id is 1
So first problem is your route is
app.delete('/api/products/delete:', productController.delete);
tha dosen't mean anything to Express. I think you want
app.delete('/api/products/:id', productController.delete);
now req.params.id should contain the parameter you send. Note im dropping the underscore here. you could use
app.delete('/api/products/:_id', productController.delete); and keep the underscore if you like.
Second mistake I think is your Angular code. you have the : in your call it should just be
$http.delete('/api/products/' + this_id)
Now you're sending the route with whatever Id you are trying to delete i.e
/api/products/1
Now Express gets that and can map it to /api/products/:id and replace the id and now your controller should work. barring any other issues.
Edit
I'm not very familiar with Angular but I think the reason people are saying to use $resource is it is easier. You can directly call the different HTTP verbs directly on the objects themselves objects like
product.update and product.delete rather than trying to craft the http calls yourself. I'm sure there is a lot more to it than that but its a feature that's built into Angular that can be leveraged. I think one of the catches is the URLs for the resources just have to be set up a specific way on the server but I believe there was a way to override them in Angular.
I'm going to make web application (SPA) with:
Backend: Node.js (express)
Frontend: Jade + AngularJS
Database: Mongoose
I will send data (as a form) to backend in this way ExpressJS AngularJS POST (Check ANSWER)
It will be simple CRUD.
However i wondering how should I display data from backend?
For example:
I'll run application
var Partner = require('../model/partners');
router.get('/', function (req, res) {
Partner.find({}, function (err, partnerList) {
if (err) throw err;
res.render('campaign', {
partnerList: partnerList
});
});
});
And how should i display data (partnerList). Maybe in this way?
- each item in partnerList
= item.name
Or maybe there is another better way with angular to display data at view? I'm asking because later i'd like remove or update items from partnerList (CRUD operation). And it may be a problem because i will have to send item._id as a parameter to angular function?
For example if i will add button to remove record:
- each item in partnerList
= item.name
button(type='remove' ng-click="sub('#{item._id}')")
script.
app.controller('view1Ctrl', function($scope, $http) {
$scope.sub = function(id) {
$http.post('/',id).
success(function(data) {
console.log("posted successfully");
}).error(function(data) {
console.error("error in posting");
})
}
});
Probably it won't work correct
As said in previous coment, from my point of view I prefere to send the minimum required data from the backend to the client, but it depends of you infrastructure and you concurrent users.
Example 1:
You have a web app with +5K concurrent users, in this case is better handle all the huge stuff at frondend side or you will need to spend a lot of money in your backend hardware.
Practical case:
Users POST a new comment in a blog page. You sanitize the text string at the backend and put it at you preferred datastore... But JUST respond with a simple json like {"status": "ok"}. If the frond end recive this, modify the DOM with the text string that the client sent to the backend in the POST stage, but not send again all the HTML with this (for example) 500 characters comment.
If server responds with {"status":"error"}, modify the DOM to let the user know what's the problem about his comment (more specified json message {"status":"error", "data":"you comment is bigger than 500 chars"})
Problems:
You need extra frontend code to handle these situations in the client side. So this "maybe" will inpact on the user the 1st time that it visits your page.
Pros:
Less hardware costs
Overall less server response times.
More user interactive website modeling only certain parts of the DOM at any moment.
...
Example 2:
You have a simple page with low concurrent users. Then you choose. Let you backend to handle everything? Or keep working with json responses?
I always use the 1st example. Hope this helps in your question.
I think the preferred method would be to set up a second route from express to specifically render JSON, then use angular's $http method to get that data and use it in your controller. If you want to do it with a single route, you can pass the JSON data as a string to your view on the server-side, but it might get a little unruly.
// app.js
...
partnerList: JSON.stringify(partnerList);
...
// index.jade
div(ng-repeat="item in partnerList")
p {{ item.name }}
button(type='remove', ng-click="sub(item._id)")
...
script.
app.controller('view1Ctrl', function($scope, $http) {
$scope.partnerList = JSON.parse(#{partnerList});
...
EDIT To use the JSON string example, you would have to render using the Unbuffered Code syntax. But I'm not sure how you would do that inside a script. block. To instead go the route of serving JSON separately, change your server routes to this:
var Partner = require('../model/partners');
router.get('/', function (req, res) {
res.render('campaign');
});
router.get("/partner-list", function(req, res) {
Partner.find({}, function (err, partnerList) {
if (err) throw err;
res.json({ partnerList: partnerList });
});
});
Then your angular code will query that /partner-list path with $http.get().
script.
app.controller('view1Ctrl', function($scope, $http) {
$http.get("/partner-list").then(function(result) {
$scope.partnerList = result.data.partnerList;
});
...
});
My controller action:
//...
this.create = function (req, res) {
return res.redirect('http://www.example.com');
}
//..
Using Jasmine how can I assert that the action redirects to http://www.example.com? Should I supply a mock res in order to perform assertions on it using Jasmine spies?
So, one way to test is
expect(response.request.href).toContain("redirected");
However, this actually navigates to the endpoint, so if you want to test an absolute url, you actually navigate to that endpoint, and therefore you are subject to any additional redirects and/or internet issues
Is it possible to test an Express JS REST API using supertest but replacing the actual database connection with a mock database object? I have unit tests covering the database models and other parts of the application as well as functional tests of the API endpoints making actual database connections, but I have a weird requirement to create integration tests that are like the functional tests but use mock database connections. A sample endpoint controller is below:
var model = require('../../../lib/models/list');
module.exports = {
index: function(req, res) {
var data = { key: 'domains', table: 'demo.events'};
var dataModel = new model(data);
dataModel.query().then(function(results) {
res.respond({data: results}, 200);
}).fail(function(err) {
console.log(err);
res.respond({message: 'there was an error retrieving data'}, 500);
});
}
};
And the index for the URI is
var express = require('express'), app, exports;
app = exports = module.exports = express();
exports.callbacks = require('./controller');
app.get('/', exports.callbacks.index);
The list model used in the controller connects to the database and retrieves the data that is output. The challenge is mocking that actual database call while still using supertest to make the request and retrieve the data from the URI
Any information would be helpful including if you think this is a bad or pointless idea
I have had limited success with 2 approaches:
1) use rewire to replace the database driver library like mongodb with a mocked one, perhaps using the spy/stub/mock capabilities of sinon
2) Set your db as an app setting via app.set('mongodb', connectedDb) for dev/prod but in test environment set a mock database instead. This requires your db-accessing code (models typically) to get the DB from the app, or otherwise be mock-friendly or designed with a dependency injection pattern.
Neither of these make everything clean and painless, but I have gotten some utility out of them.