본문 바로가기

Back-end/Mongodb

mongoose 테스트 및 node 연동

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' "
},
 

데이터 정의

//in todo.js 
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} = {todo: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')
            //200이여하며 
            .expect(200)
            .expect((res) => {
              //리턴값이 2개 이상이여한다. 
                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);
 
 
//테스트를 위한 디비 분기 
//package.json 파일에 대해 분기되어있음 
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('mongodb://localhost:27017/TodoApp'); 
 
//MONGODB_URI -> 실서버 데이터베이스가 있을 경우에만 
mongoose.connect(process.env.MONGODB_URI)
 
 
module.exports = {mongoose};