nodejs와 moongoose mocha
moongoose
moongoose는 데이터 맵핑 프레임워크이다.
node.js에서 데이터를 쉽게 사용할 수 있도록 돕는다.
package.json
"scripts": {
"start": "node server/server.js",
"test": "export NODE_ENV=test || SET NODE_ENV=test && mocha server/**/*.test.js",
"test-watch": "nodemon --exec 'npm test' "
},
데이터 정의
var mongoose = require('mongoose');
var Todo = mongoose.model('Todo', {
text: {
type: String,
required: true,
minlength: 1,
trim: true
},
completed: {
type: Boolean,
default: false
},
completedAt: {
type: Number,
default: null
}
});
module.exports = {Todo};
위와 같이 사용할 DB를 정의하여준다.
server.js
var {Todo} = require('./models/todo');
todo 데이터를 가져와 사용한다.
테스팅을 위한 준비
const {app} = require('./../server');
미리 테스트할 내용을 가져온다.
데이터 조회
기본소스
app.get('/todos', (req, res)=> {
Todo.find().then((todos)=> {
res.send({todos});
}, (e)=> {
res.status(400).send(e);
});
});
mocha testing
const todos = [{
_id: new ObjectID(),
text: 'First test todo',
complete: true
}, {
_id: new ObjectID(),
text: 'Second test todo',
complete: true
}];
beforeEach((done) => {
Todo.remove({}).then(() => {
return Todo.insertMany(todos);
}).then(() => done());
});
describe('GET /todos', () => {
it('should get all todos', (done) => {
request(app)
.get('/todos')
.expect(200)
.expect((res) => {
expect(res.body.todos.length).toBe(2);
})
.end(done);
});
});
describe('GET /todos/:id', () => {
it('should return todo doc', (done) => {
request(app)
.get(`/todos/${todos[0]._id.toHexString()}`)
.expect(200)
.expect((res) => {
expect(res.body.todo.text).toBe(todos[0].text);
})
.end(done);
});
it('should return 404 if todo not found', (done) => {
var hexId = new ObjectID().toHexString();
request(app)
.get(`/todos/${hexId}`)
.expect(404)
.end(done);
});
it('should return 404 for non-object ids', (done) => {
request(app)
.get('/todos/123abc')
.expect(404)
.end(done);
});
});
데이터 등록
소스코드
app.post('/todos', (req, res) => {
var todo = new Todo({
text: req.body.text
});
todo.save().then((doc) => {
res.send(doc);
}, (e) => {
res.status(400).send(e);
});
});
테스팅소스
describe('POST /todos', () => {
it('should create a new todo', (done) => {
var text = 'Test todo text';
request(app)
.post('/todos')
.send({text})
.expect(200)
.expect((res) => {
expect(res.body.text).toBe(text);
})
.end((err, res) => {
if (err) {
return done(err);
}
Todo.find({text}).then((todos) => {
expect(todos.length).toBe(1);
expect(todos[0].text).toBe(text);
done();
}).catch((e) => done(e));
});
});
it('should not create todo with invalid body data', (done) => {
request(app)
.post('/todos')
.send({})
.expect(400)
.end((err, res) => {
if (err) {
return done(err);
}
Todo.find().then((todos) => {
expect(todos.length).toBe(2);
done();
}).catch((e) => done(e));
});
});
});
데이터 삭제
소스코드
app.delete('/todos/:id', (req, res) => {
var id = req.params.id;
if (!ObjectID.isValid(id)) {
return res.status(404).send();
}
Todo.findByIdAndRemove(id).then((todo) => {
if (!todo) {
return res.status(404).send();
}
res.send({todo});
}).catch((e) => {
res.status(400).send();
});
});
테스팅소스
describe('DELETE /todos/:id', () => {
it('should remove a todo', (done) => {
var hexId = todos[1]._id.toHexString();
request(app)
.delete(`/todos/${hexId}`)
.expect(200)
.expect((res) => {
expect(res.body.todo._id).toBe(hexId);
})
.end((err, res) => {
if (err) {
return done(err);
}
Todo.findById(hexId).then((todo) => {
expect(todo).toNotExist();
done();
}).catch((e) => done(e));
});
});
it('should return 404 if todo not found', (done) => {
var hexId = new ObjectID().toHexString();
request(app)
.delete(`/todos/${hexId}`)
.expect(404)
.end(done);
});
it('should return 404 if object id is invalid', (done) => {
request(app)
.delete('/todos/123abc')
.expect(404)
.end(done);
});
});
데이터 수정
app.patch('/todos/:id', (req,res)=>{
var id = req.params.id;
var body = _.pick(req.body, ['text','completed']);
if(!ObjectID.isValid(id)){
return res.status(404).send("invalid");
}
if(_.isBoolean(body.completed) && body.completed){
body.completedAt = new Date().getTime();
}else{
body.completed = false;
body.completedAt = null;
}
Todo.findByIdAndUpdate(id, {$set:body}, {new:true}).then((todo)=>{
if(!todo){
return res.status(404).send("noTodo");
}
res.send({todo});
}).catch((e)=>{
res.status(400).send(e);
});
});
테스팅 소스
describe('PATCH /todos/:id', () => {
it('should update the todo', (done) => {
var hexId = todos[0]._id.toHexString();
var text = 'This should be the new text';
request(app)
.patch(`/todos/${hexId}`)
.send({
completed: true,
text
})
.expect(200)
.expect((res) => {
expect(res.body.todo.text).toBe(text);
expect(res.body.todo.completed).toBe(true);
expect(res.body.todo.completedAt).toBeA('number');
})
.end(done);
});
it('should clear completedAt when todo is not completed', (done) => {
var hexId = todos[1]._id.toHexString();
var text = 'This should be the new text!!';
request(app)
.patch(`/todos/${hexId}`)
.send({
completed: false,
text
})
.expect(200)
.expect((res) => {
expect(res.body.todo.text).toBe(text);
expect(res.body.todo.completed).toBe(false);
expect(res.body.todo.completedAt).toNotExist();
})
.end(done);
});
});
테스트 설정
테스트를 할 경우 동일한 디비를 사용하게되면
문제가 있을 수 있으므로 이부분을 분기하여준다.
config
var env = process.env.NODE_ENV || 'development';
console.log('env *****',env);
if (env === 'development'){
process.env.PORT = 3000;
process.env.MONGODB_URI = 'mongodb://localhost:27017/TodoApp';
}else if( env === 'test'){
process.env.PORT = 3000;
process.env.MONGODB_URI = 'mongodb://localhost:27017/TodoAppTEST'
}
db/mongoose.js
var mongoose = require('mongoose');
mongoose.Promise = global.Promise;
mongoose.connect(process.env.MONGODB_URI)
module.exports = {mongoose};