2016년 3월 6일 일요일

[MEANI-Stack] - #10 (Node & Express) User API Test작성

[MEANI-Stack] - #10 (Node & Express) User API Test작성




API test를 하기위해 jasmine Framework를 선택 하였습니다.

jasmine framework는 javascript test framework로써
mochaJS, chaiJS 와 같은 테스트 프레임워크들 사이에 있습니다.

과거 jasmine 1.x시절에는 done() callback메소드
즉 promise스펙을 지원하지 않아
promise 스펙을 지원하는 mochaJS가 압도적으로 우세했지만

jasmine 2.0에서 비동기 콜백 함수 지원, 즉 promise spec을 구현함에 따라
mochaJS + chaiJS promise 조합으로 사용하던 스택과  동등한 위치가 되었지요.

이에따라 angularJS팀에서 만든 protractor 테스트 프레임워크가
기존에 mocha와 jasmine을 모두 지원하다가
공식적으로 mocha를 더이상 지원하지 않겠다고 발표하면서
jasmine의 관심도가 더 높아지게 되었습니다.



protractor link : https://angular.github.io/protractor/#/
mocha limited support link : https://github.com/angular/protractor/blob/master/docs/frameworks.md



이번 펫 프로젝트 기술 스택이 MEANI 즉 angularJS를 사용하는만큼
위와같은 이유들로 protractor를 고려하여 jasmine으로 선택하였습니다.

jasmine 기반으로 제작된 jasmine-node를 사용하려 해 보았으나
몇일간 테스트 해본결과 순수한 jasmine과의 큰 차이를 느낄수 없어
jasmine을 사용합니다.
jasmine 링크 : http://jasmine.github.io/

jasmine-node 링크 : https://github.com/mhevery/jasmine-node/tree/Jasmine2.0



아래 명령어로 jasmin과 request 모듈을 설치합니다.
$ npm install -g jasmine
$ npm install -save request


jasmine으로 http request를 요청하기 위해서는 매번 XMLHttpRequest 객체를 직접 생성해야 하므로
request 모듈도 함께 설치해줍니다.
request 모듈은 simple http request를 제공해줍니다.



CRUD 테스트를 작성하기 위해 아래와 같이 user controller에 remove를 추가해 줍니다.

user.server.controller.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
var User = require('mongoose').model('User');
 
exports.create = function(req, res, next) {
  var user = new User(req.body);
  console.log(req.body);
 
  User.count({email: req.body.email},
    function (err, count) {
      if (!count) {
        user.save(function(err) {
          if (err) {
            res.json(err);
          } else {
            res.json(user);
          }
        });
      } else {
        var errorMessage = 'User email is Already Exists';
        console.log(errorMessage);
        res.json(errorMessage);
      }
    });
};
 
exports.list = function(req, res, next) {
  User.find({}, function(err, users) {
    if (err) {
      return next(err);
    } else {
      res.json(users);
    }
  });
};
 
exports.findUserByEmail = function(req, res, next) {
  User.findOne({
    email: req.params.email
  }, function(err, user) {
    if (err) {
      console.log(err);
      return next(err);
    } else {
      console.log(err);
      res.json(user);
    }
  });
}
 
exports.remove = function(req, res, next) {
  User.remove({
    email: req.params.email
  }, function(err, user) {
    if (err) {
      return next(err);
    } else {
      res.json(user);
    }
  });
}
 
 
cs


mongoose에서 제공하는 기본 메소드들은 model.js를 사용합니다.
기본 제공하는 메소드 들에 대해 궁굼하다면 아래 링크를 참고하세요.
http://mongoosejs.com/docs/api.html#model-js


user.server.routes.js에 아래와같이 delete를 추가해 줍니다.

1
2
3
4
5
6
7
8
9
10
11
12
 
var users = require('../../app/controllers/users.server.controller.js');
 
module.exports = function(app) {
  app.route('/users')
    .post(users.create)
    .get(users.list);
  app.route('/users/:email')
    .get(users.findUserByEmail)
    .delete(users.remove);
}
 
cs



이제 본격적으로 jasmine을 사용하기 위해 아래 명령어를 입력합니다.

$ jasmine init
$ jasmine examples

위 명령어들을 실행하면 spec directory가 생성되고 간단한 예제가 생성됩니다.
spec/
 -helpers/
 -jasmine_examples/
 -support/

최종적으로 위와 같은 directory가 생성되는데
저는 여기에 모델들을 테스트 하기 위한 코드를 모아놓은 models directory와 
순수하게 jamsine을 테스트를 해보기위한 test directory두개를 더 생성했습니다.


spec/
 -helpers/
 -jasmine_examples/
 -support/
 -models/
 -test/


그리고 models 안에 user.model.spec.js를 생성하고 아래와 같이 코드를 작성하였습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
 
var request = require('request');
 
// jasmine.getEnv().defaultTimeoutInterval = 500;
var r = request.defaults({'proxy':'http://localhost:4000/users'});
 
var requestDefault = 'http://localhost:4000';
 
describe('User Model Test'function() {
 
  var email = 'test_' + Math.floor(Math.random()*10000+ '@gmail.com';
  var form = {
    email : email,
    password : '12345678',
    role : 'User'
  }
 
  var responseCallback = function(err, res, body, debug) {
    if (debug) {
      console.log(res);
    }
    expect(res.statusCode).toEqual(200);
    if (res.statusCode !== 200){
      console.log(res.statusCode);
      console.log(body);
    }
  };
 
  it('user list get'function(done) {
    request.get(requestDefault + '/users'function(err, res, body) {
      responseCallback(err, res, body);
      done();
    });
  });
 
  it('user create'function(done) {
    request.post({
        url : requestDefault + '/users',
        form : form,
        headers : {
          'Content-Type' : 'application/json'
        }
      }, function(err, res, body) {
        responseData = JSON.parse(body);
        expect(responseData.email).toEqual(email);
        responseCallback(err, res, body);
        done();
      });
  });
 
  it('user read'function(done) {
    request.get(requestDefault + '/users/' + email, function(err, res, body) {
      responseData = JSON.parse(body);
      expect(responseData.email).toEqual(email);
      responseCallback(err, res, body);
      done();
    });
  });
 
  it('user delete'function(done) {
    request.del(requestDefault + '/users/' + email, function(err, res, body) {
      responseData = JSON.parse(body);
      // console.log(body);
      expect(responseData.n).toEqual(1);
      responseCallback(err, res, body);
      done();
    });
  });
 
});
 
 
 
cs




실행하는법은 project directory에서 jasmine 명령어를 입력하면 됩니다.
이때 주의할점은 junit처럼 가상서버를 자동으로 생성해서 테스트를 진행하는게 아니라서
node server가 실행되지 않은 상태에서 테스트를 실행할경우 request요청은 모두 fail이 됩니다.




훌륭합니다. 0.066초 만에 user.model CRUD를 모두 테스트 완료하였습니다.
단순계산으로 해보면 모델이 10개일경우에도 1초 미만에 테스트가 가능해지니
테스트 작성이 조금 번거로울지 몰라도 1시간에 10번씩 테스트해도 아무런 부담이 없어졌습니다.



작업의 마무리는 커밋!
https://github.com/polyglotm/PP-MEANI-stack-back-end


포스트의 마무리는 애자일 보드 확인!



다음 태스크는 (Angular 2) 회원가입 UI 작성 으로 선택 되었습니다.
드디어! 고대하던 Angular 2를 본격적으로 시작할수 있게 되었네요!

한가지 고민이 있다면 ionic2를 위해서 굳이 따로 mobile UI를 따로 만들어야 하는건가 생각이 듭니다.
가급적이면 웹에서 mobile대응을 하고
ionic은 말그대로 컨버팅 되는것에 중점을 둘까 생각중입니다.

물론  ionic2가 컨버팅이 잘 된다면요...

댓글 없음:

댓글 쓰기