mocha 및 node.js를 사용하여 개인 함수의 단위 테스트


131

node.js로 작성된 응용 프로그램을 단위 테스트하기 위해 mocha를 사용하고 있습니다.

모듈로 내 보내지 않은 함수를 단위 테스트 할 수 있는지 궁금합니다.

예:

나는 이와 같이 정의 된 많은 기능을 가지고있다. foobar.js

function private_foobar1(){
    ...
}

function private_foobar2(){
    ...
}

공개로 내 보낸 몇 가지 함수 :

exports.public_foobar3 = function(){
    ...
}

테스트 케이스는 다음과 같이 구성됩니다.

describe("private_foobar1", function() {
    it("should do stuff", function(done) {
        var stuff = foobar.private_foobar1(filter);
        should(stuff).be.ok;
        should(stuff).....

private_foobar1내 보내지 않기 때문에 분명히 작동하지 않습니다 .

개인 메소드를 단위 테스트하는 올바른 방법은 무엇입니까? 모카에는 기본 제공 방법이 있습니까?


답변:


64

모듈에서 함수를 내 보내지 않으면 모듈 외부의 테스트 코드로 함수를 호출 할 수 없습니다. 이는 JavaScript가 작동하는 방식 때문이며 Mocha 자체로는이를 피할 수 없습니다.

개인 함수 테스트가 옳은 일이라고 결정한 몇 가지 경우, 내가 한 것은 모듈이 테스트 설정에서 실행 중인지 여부를 확인하기 위해 확인하는 환경 변수를 설정하는 것입니다. 테스트 설정에서 실행되면 테스트 중에 호출 할 수있는 추가 기능을 내 보냅니다.

여기서 "환경"이라는 단어가 느슨하게 사용되었습니다. 이는 process.env"지금 테스트 중입니다"라는 모듈과 통신 할 수있는 검사 또는 다른 것을 의미 할 수 있습니다. 내가해야했던 인스턴스는 RequireJS 환경에 있었고이 module.config목적으로 사용 했습니다.


2
조건부 내보내기 값은 ES6 모듈과 호환되지 않는 것 같습니다. 내가 SyntaxError: 'import' and 'export' may only appear at the top level
간다

1
예 @aij ES6의 정적 수출로 인해 당신은 사용할 수 없습니다 import, export블록의 내부. 결국 시스템 로더를 사용하여 ES6에서 이러한 종류의 작업을 수행 할 수 있습니다. 이제 문제를 해결하는 한 가지 방법은 module.exports = process.env.NODE_ENV === 'production' ? require('prod.js') : require('dev.js')es6 코드 차이를 해당 파일 에 사용 하고 저장하는 것입니다.
cchamberlain 2016 년

2
당신이 전체 범위를 가졌다면 노출 여부에 관계없이 모든 개인 기능을 테스트하고 있다고 생각합니다.
Ziggy

1
@aij 조건부로 내보낼 수 있습니다 ...이 답변을 참조하십시오 : stackoverflow.com/questions/39583958/…
RayLoveless

187

배선 모듈을 점검하십시오 . 모듈 내에서 개인 변수와 함수를 얻거나 조작 할 수 있습니다.

따라서 귀하의 경우 사용법은 다음과 같습니다.

var rewire = require('rewire'),
    foobar = rewire('./foobar'); // Bring your module in with rewire

describe("private_foobar1", function() {

    // Use the special '__get__' accessor to get your private function.
    var private_foobar1 = foobar.__get__('private_foobar1');

    it("should do stuff", function(done) {
        var stuff = private_foobar1(filter);
        should(stuff).be.ok;
        should(stuff).....

3
@Jaro 대부분의 코드는 AMD 모듈의 형태로되어 있는데,이 모듈은 rewire가 처리 할 수 없습니다 (AMD 모듈은 기능이지만 rewire는 "기능 내 변수"를 처리 할 수 ​​없기 때문에). 또는 재 와이어가 처리 할 수없는 다른 시나리오가 변환되었습니다. 실제로 rewire를 보려는 사람들은 사용하기 전에 먼저 제한을 읽으십시오 (이전에 링크 됨). a) "비공개"항목을 내 보내야하고 b) 재 와이어 제한에 빠지지 않는 단일 앱이 없습니다.
Louis

1
작은 점으로, 코드 범위는 이와 같이 작성된 테스트를 선택하지 못할 수 있습니다. 적어도 Jest의 내장 범위 도구를 사용하여 본 것입니다.
Mike Stead

Rewire는 jest의 자동 조롱 도구와도 잘 작동하지 않습니다. 나는 여전히 jest의 이점을 활용하고 일부 개인 변수에 액세스하는 방법을 찾고 있습니다.
btburton42

그래서이 작업을 시도했지만 typescript를 사용하고 있는데이 문제를 일으키는 것으로 추측됩니다. 기본적으로 다음과 같은 오류가 발생 Cannot find module '../../package' from 'node.js'합니다. 이것에 익숙한 사람이 있습니까?
clu

재배 선에서 잘 작동한다 .ts, typescript내가 사용하여 실행 ts-node @clu
muthukumar selvaraj

24

다음은 블로그의 Google 엔지니어 인 필립 월튼 (Philip Walton)이 설명한 개인 분석법을 테스트하기위한 훌륭한 워크 플로우 입니다.

원리

  • 정상적으로 코드 작성
  • 개별 메소드를 별도의 코드 블록으로 객체에 바인딩 _하고 예를 들어
  • 시작 및 종료 주석으로 해당 코드 블록을 둘러 쌉니다.

그런 다음 빌드 작업 또는 자체 빌드 시스템 (예 : grunt-strip-code)을 사용하여 프로덕션 빌드를 위해이 블록을 제거하십시오.

테스트 빌드는 개인 API에 액세스 할 수 있으며 프로덕션 빌드는 액세스 할 수 없습니다.

단편

다음과 같이 코드를 작성하십시오.

var myModule = (function() {

  function foo() {
    // private function `foo` inside closure
    return "foo"
  }

  var api = {
    bar: function() {
      // public function `bar` returned from closure
      return "bar"
    }
  }

  /* test-code */
  api._foo = foo
  /* end-test-code */

  return api
}())

그리고 당신의 그런 임무는

grunt.registerTask("test", [
  "concat",
  "jshint",
  "jasmine"
])
grunt.registerTask("deploy", [
  "concat",
  "strip-code",
  "jshint",
  "uglify"
])

더 깊은

후반 기사에서는 "비공개 메소드 테스트"의 "이유"에 대해 설명합니다.


1
또한 비슷한 워크 플로를 지원할 수있는 웹킷 플러그인을 찾았습니다. webpack-strip-block
JRulle

21

당신은 간단하게하기 원하는 경우, 단지뿐만 아니라 private 멤버를 내보낼 수 있지만, 분명히으로 어떤 규칙으로 공개 API에서 예를 들어 접두사를 분리 _한 아래 또는 둥지를 개인 객체입니다.

var privateWorker = function() {
    return 1
}

var doSomething = function() {
    return privateWorker()
}

module.exports = {
    doSomething: doSomething,
    _privateWorker: privateWorker
}

7
전체 모듈이 실제로 개인용이며 일반적인 소비 용이 아닌 경우 에이 작업을 수행했습니다. 그러나 범용 모듈의 경우 코드 를 테스트 할 때만 테스트에 필요한 것을 노출하는 것을 선호합니다 . 궁극적으로 테스트 환경을 위조하여 누군가가 사물에 접근하는 것을 막을 수있는 것은 없지만 사실 자신의 응용 프로그램에서 디버깅을 수행 할 때 필요하지 않은 기호를 보지 못합니다. 공개 API의 일부입니다. 이런 식으로 API가 의도하지 않은 목적으로 API를 남용하려는 유혹이 없습니다.
Louis

2
중첩 구문을 사용할 수도 있습니다. {... private : {worker : worker}}
Jason

2
모듈이 모두 순수한 기능이라면이 작업을 수행하는 데 단점이 없습니다. 상태를 유지하고 변경하는 경우주의하십시오.
Ziggy

5

: 나는 당신이 유용하다는 사실을 발견이 목적을 위해 NPM 패키지를 만들 필요-에서

기본적으로 다음과 같은 방법으로 비공개 메소드를 공개합니다.

module.testExports = {
    private_foobar1: private_foobar1,
    private_foobar2: private_foobar2,
    ...
}

참고 : 물론 testExports원하는 이름을 사용할 수 있습니다 exports.

그리고 다른 모듈에서 :

var requireFrom = require('require-from');
var private_foobar1 = requireFrom('testExports', './path-to-module').private_foobar1;

1
이 방법에는 실질적인 이점이 없습니다. "비공개"기호를 더 비공개로 만들지는 않습니다. (모두 requireFrom가 올바른 매개 변수를 사용하여 호출 할 수 있습니다 .) 또한 모듈 textExports이로드 되기 전에require 호출에 의해 모듈 이로드 되면를 반환 합니다. 방금 테스트를 마쳤습니다. 모듈의로드 순서를 제어하는 ​​것이 가능하지만 항상 실용적이지는 않습니다. (SO에 대한 일부 Mocha 질문에서 알 수 있듯이)이 솔루션은 일반적으로 AMD 유형 모듈에서는 작동하지 않습니다. (테스트를 위해 매일 노드에 AMD 모듈을로드합니다.) requireFromrequireFromundefined
Louis

AMD 모듈에서는 작동하지 않습니다! Node.js는 common.js를 사용하고 AMD를 사용하도록 변경하면 표준을 벗어난 것입니다.
jemiloii

@JemiloII 수백 명의 개발자가 Node.js를 매일 사용하여 AMD 모듈을 테스트합니다. 그렇게하는 데있어 "표준 밖"은 없습니다. Node.js에는 AMD 로더가 포함되어 있지 않지만 Node가 개발자가 개발하고자하는 형식을로드하기 위해 로더를 확장 할 수있는 명시 적 후크를 제공한다는 점을 알 수 있습니다.
Louis

그것은 표준을 벗어났습니다. amd 로더를 수동으로 포함 해야하는 경우 node.js의 표준이 아닙니다. node.js 코드에 AMD가 거의 보이지 않습니다. 브라우저에서 볼 수 있지만 노드입니다. 아니요. 나는 그것이 끝나지 않았다고 말하는 것이 아닙니다. 우리가 언급하고있는 질문과 대답은 amd 모듈에 대해 아무 말도하지 않습니다. 따라서 누구든지 amd 로더를 사용한다고 말하지 않고 노드 내보내기는 amd와 작동하지 않아야합니다. 주목하고 싶지만 es6 내보내기로 commonjs가 나갈 수 있습니다. 언젠가 우리는 하나의 내보내기 방법 만 사용할 수 있기를 바랍니다.
jemiloii

4

Internal () 이라는 추가 함수를 추가하고 거기에서 모든 개인 함수를 반환합니다. 그런 다음 이 Internal () 함수를 내 보냅니다. 예:

function Internal () {
  return { Private_Function1, Private_Function2, Private_Function2}
}

// Exports --------------------------
module.exports = { PublicFunction1, PublicFunction2, Internal }

다음과 같이 내부 함수를 호출 할 수 있습니다.

let test = require('.....')
test.Internal().Private_Function1()

나는이 솔루션을 가장 좋아합니다.

  • Internal () 함수 는 항상 하나만 내보내집니다. 이 Internal () 함수는 항상 개인 함수를 테스트하는 데 사용됩니다.
  • 구현이 간단합니다
  • 생산 코드에 대한 영향이 적음 (한 가지 추가 기능 만)

2

@ barwin answer에 따라 단위 테스트를 수행하는 방법을 확인했습니다. 재배 선 된 모듈. 이 솔루션이 간단하게 작동하는지 확인할 수 있습니다.

모듈은 공용 및 개인의 두 부분으로 구성되어야합니다. 공용 함수의 경우 표준 방식으로 수행 할 수 있습니다.

const { public_foobar3 } = require('./foobar');

개인 범위의 경우 :

const privateFoobar = require('rewire')('./foobar');
const private_foobar1 = privateFoobar .__get__('private_foobar1');
const private_foobar2 = privateFoobar .__get__('private_foobar2');

이 주제에 대해 더 많이 알기 위해 전체 모듈 테스트로 실제 예제를 만들었습니다. 테스트에는 개인 및 공개 범위가 포함됩니다.

자세한 내용 은 주제를 자세히 설명하는 기사 ( https://medium.com/@macsikora/how-to-test-private-functions-of-es6-module-fb8c1345b25f ) 를 확인하는 것이 좋습니다 . 여기에는 코드 샘플이 포함되어 있습니다.


2

나는 이것이 반드시 당신이 찾고있는 대답은 아니라는 것을 알고 있지만, 내가 찾은 것은 개인 함수가 테스트 할 가치가 있다면 대부분 자체 파일에있을 가치가 있다는 것입니다.

예를 들어 다음과 같이 공개 메소드와 동일한 파일에 개인 메소드를 갖는 대신 ...

src / thing / PublicInterface.js


function helper1 (x) {
    return 2 * x;
}

function helper2 (x) {
    return 3 * x;
}

export function publicMethod1(x) {
    return helper1(x);
}

export function publicMethod2(x) {
    return helper1(x) + helper2(x);
}

... 당신은 이것을 다음과 같이 나누었습니다.

src / thing / PublicInterface.js

import {helper1} from './internal/helper1.js';
import {helper2} from './internal/helper2.js';

export function publicMethod1(x) {
    return helper1(x);
}

export function publicMethod2(x) {
    return helper1(x) + helper2(x);
}

src / thing / internal / helper1.js

export function helper1 (x) {
    return 2 * x;
}

src / thing / internal / helper2.js

export function helper2 (x) {
    return 3 * x;
}

이렇게 하면 Rewire 및 기타 "마법"(디버깅 중 자체 문제점이 있거나 디버깅 할 때 어려움을 겪을 때 또는 TypeScript로 이동하려고 할 때)을 사용하지 않고도 쉽게 테스트 helper1하고 helper2그대로 사용할 수 있습니다. 새로운 동료에 대한 이해도). 그리고 하위 폴더 internal또는 이와 유사한 폴더에 있으면 의도하지 않은 장소에서 실수로 사용되는 것을 피할 수 있습니다.


PS : "개인"방법과 또 다른 일반적인 문제는 당신이 테스트하려는 경우이다 publicMethod1publicMethod2와 헬퍼를 조롱 다시, 당신은 일반적으로 그렇게 재배 선 같은 것을해야합니다. 그러나 파일이 별도의 파일에 있으면 Proxyquire 를 사용 하여 이를 수행 할 수 있습니다. Rewire와 달리 빌드 프로세스를 변경할 필요가없고 읽고 디버깅하기 쉽고 TypeScript 와도 잘 작동합니다.


1

테스트에 개인 메소드를 사용할 수있게하려면 다음과 같이하십시오.

const _myPrivateMethod: () => {};

const methods = {
    myPublicMethod1: () => {},
    myPublicMethod2: () => {},
}

if (process.env.NODE_ENV === 'test') {
    methods._myPrivateMethod = _myPrivateMethod;
}

module.exports = methods;
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.