2016-08-27 4 views
5

나는 특급 4를 사용하여 Node.js를 응용 프로그램이이 내 컨트롤러 :약속을 반환하는 다른 함수를 호출하는 함수를 단위 테스트하는 방법?

var service = require('./category.service'); 

module.exports = { 
    findAll: (request, response) => { 
    service.findAll().then((categories) => { 
     response.status(200).send(categories); 
    }, (error) => { 
     response.status(error.statusCode || 500).json(error); 
    }); 
    } 
}; 

그것은 약속을 반환 내 서비스를 호출합니다. 모든 것은 작동하지만 유닛 테스트를 시도 할 때 문제가 있습니다.

기본적으로, 나는 내 서비스가 반환하는 것을 기반으로 올바른 상태 코드와 본문으로 응답을 플러시합니다.

그래서 모카와 그것을 sinon는 다음과 같은 : 기능 내가 돌아 자체 나는 다음에 내 주장을 연결할 수 있기 때문에 약속을 테스트입니다 때 나는 내 비슷한 시나리오를 테스트 한

it('Should call service to find all the categories', (done) => { 
    // Arrange 
    var expectedCategories = ['foo', 'bar']; 

    var findAllStub = sandbox.stub(service, 'findAll'); 
    findAllStub.resolves(expectedCategories); 

    var response = { 
     status:() => { return response; }, 
     send:() => {} 
    }; 
    sandbox.spy(response, 'status'); 
    sandbox.spy(response, 'send'); 

    // Act 
    controller.findAll({}, response); 

    // Assert 
    expect(findAllStub.called).to.be.ok; 
    expect(findAllStub.callCount).to.equal(1); 
    expect(response.status).to.be.calledWith(200); // not working 
    expect(response.send).to.be.called; // not working 
    done(); 
}); 

.

나는 또한 controller.findAll을 Promise로 랩핑하고 response.send에서 해결하려고 시도했지만 어느 것도 작동하지 않았다.

+1

약속 반환 함수를 호출하는 모든 함수는 비동기이며 약속 자체를 반환해야합니다. 콜백 만받는다면 콜백 기반 API를 테스트해야합니다. – Bergi

+0

Chai를 사용하고 있습니까? 그렇다면 http://chaijs.com/plugins/chai-as-promised/ –

+0

예하지만 테스트중인 기능은 약속의 아들 체인을 약속대로 반환하지 않습니다. 단순한 도움이되지 않습니다. – jbernal

답변

6

당신은 모든 비동기 작업이 주장하기 전에 완료 확인하기 위해 res.send 방법으로 어설 섹션을 이동해야합니다 :

var response = { 
    status:() => { return response; }, 
    send:() => { 
    try { 
     // Assert 
     expect(findAllStub.called).to.be.ok; 
     expect(findAllStub.callCount).to.equal(1); 
     expect(response.status).to.be.calledWith(200); // not working 
     // expect(response.send).to.be.called; // not needed anymore 
     done(); 
    } catch (err) { 
     done(err); 
    } 
    }, 
}; 
+0

예, 이제 작동합니다. 그러나 테스트에 실패하면 (204로 상태를 비교하자), 실패한 어설 션을 표시하지 않습니다. 그것은 단지 타임 아웃하고 말합니다 :'Error : 2000ms의 타임 아웃을 초과했습니다. 이 테스트에서 done() 콜백이 호출되고 있음을 확인하십시오. – jbernal

+0

@jbernal 내 잘못하면 던진 오류를 잡아 'done'에 전달해야 함을 잊었습니다. 편집 된 대답 –

+0

고마워요. try/catch 블록을 사용하면 나를 구할 수 있습니다. – phillyslick

1

여기서 아이디어 란 service을 호출하지 않고도 service.findAll()이 테스트 코드 내에서 액세스 할 수 있다는 약속을하는 것입니다. 지금까지 내가 볼 수있는 sinon-as-promised은 아마도 그렇게하는 것을 허용하지 않습니다. 그래서 난 그냥 네이티브 Promise (노드 버전이 너무 오래 되었으면 좋겠다)을 사용했다.

const aPromise = Promise.resolve(expectedCategories); 
var findAllStub = sandbox.stub(service, 'findAll'); 
findAllStub.returns(aPromise); 

// response = { .... } 

controller.findAll({}, response); 

aPromise.then(() => { 
    expect(response.status).to.be.calledWith(200); 
    expect(response.send).to.be.called;  
}); 
+0

이 시나리오를 알고 있습니다. 그러나 만약 당신이 깨달으면, 나의'controller.findAll'은 약속을 되풀이하지 않는다. 그리고 따라서, 내 문제. 나는 그때를 수행 할 수 없다. – jbernal

+0

@ jbernal, 귀하의 요지를 봅니다. 나는'findAll'을 뒤섞었다. 'controller.findAll()'을 호출하고 그 뒤에'service.findAll()', 즉 'constoller.findAll();을 호출하면 도움이되지 않습니다. service.findAll(). then (() => { expect (....) })' –

+0

그렇게 생각하지 마라. controller.findAll은'service.findAll'을 호출하는 것이다. 그것은 테스트 대상 중 하나이기 때문에 명시 적으로 호출하고 싶지는 않습니다. – jbernal

1

코드가 다른이있을 수 있음을 표시 할 수 있습니다 테스트하기가 어렵다 쉽게 테스트 할 수있는 가능성을 탐구 해보십시오. 점프 아웃은 service이 모듈에 포함되어 있고 종속성이 전혀 노출되지 않는다는 것입니다. 나는 당신의 코드를 그대로 테스트하는 방법을 찾는 것이 아니라 최적의 디자인을 찾는 것이 목표가되어야한다고 생각합니다.

IMO 목표는 findAll의 로직을 동 기적으로 독립적으로 테스트 할 수 있도록 service을 테스트하는 방법을 찾아서 스텁 된 구현을 제공 할 수 있도록하는 것입니다.

이 작업을 수행하는 한 가지 방법은 mockery 또는 rewire과 같은 라이브러리를 사용하는 것입니다. 둘 다 상당히 사용하기 쉽습니다. (내 경험에 의하면 테스트 스위트와 모듈 수가 늘어남에 따라 조롱이 심해지고 관리하기가 어려워집니다.) findAll 정의 된 자체 서비스 객체를 제공하여 var service = require('./category.service');을 패치 할 수 있습니다 .

또 다른 방법은 발신자에게 service을 어떤 식 으로든 노출 시키도록 코드를 재구성하는 것입니다. 그러면 발신자 (단위 테스트)가 자체 service 스텁을 제공 할 수 있습니다.

이 작업을 수행하는 쉬운 방법 중 하나는 객체 대신 함수 contstructor를 내보내는 것입니다.

module.exports = (userService) => { 

    // default to the required service 
    this.service = userService || service; 

    this.findAll = (request, response) => { 
    this.service.findAll().then((categories) => { 
     response.status(200).send(categories); 
    }, (error) => { 
     response.status(error.statusCode || 500).json(error); 
    }); 
    } 
}; 

var ServiceConstructor = require('yourmodule'); 
var service = new ServiceConstructor(); 

이제 시험은 service에 대한 스텁을 생성하고 findAll 방법을 행사하기 위해 ServiceConstructor에 제공 할 수 있습니다. 비동기 테스트의 필요성 제거.

+0

답변 해 주셔서 대단히 감사합니다. 이것들은 내가 조사 할 필요가있는 아이디어입니다. 첫 번째 노드 애플리케이션에서 작업하고 있으며 코드를 개선하기 위해 따라야 할 최적의 구조적 패턴이 더 많다고 확신합니다. 나는 그들을 구현하려고 노력할 것이다. 건배 – jbernal

관련 문제