Mocha / Chai expect.to.throw는 던진 오류를 포착하지 않습니다.


257

Chai가 expect.to.thrownode.js 앱 테스트에서 작동하는 데 문제가 있습니다. 테스트는 던진 오류에서 계속 실패하지만 테스트 케이스를 랩하여 시도하고 잡은 오류를 주장하면 작동합니다.

않는 expect.to.throw나는 그것을해야 또는 뭔가 생각처럼 작동하지?

it('should throw an error if you try to get an undefined property', function (done) {
  var params = { a: 'test', b: 'test', c: 'test' };
  var model = new TestModel(MOCK_REQUEST, params);

  // neither of these work
  expect(model.get('z')).to.throw('Property does not exist in model schema.');
  expect(model.get('z')).to.throw(new Error('Property does not exist in model schema.'));

  // this works
  try { 
    model.get('z'); 
  }
  catch(err) {
    expect(err).to.eql(new Error('Property does not exist in model schema.'));
  }

  done();
});

실패:

19 passing (25ms)
  1 failing

  1) Model Base should throw an error if you try to get an undefined property:
     Error: Property does not exist in model schema.

답변:


339

에 함수를 전달해야합니다 expect. 이처럼 :

expect(model.get.bind(model, 'z')).to.throw('Property does not exist in model schema.');
expect(model.get.bind(model, 'z')).to.throw(new Error('Property does not exist in model schema.'));

당신이 그것을하는 방식으로, 당신은 전화 expect결과 로 전달됩니다 model.get('z'). 하지만 뭔가 발생 여부를 테스트하려면에 기능 통과해야 expect, expect자신을 호출됩니다. bind상기 이용 방법은 호출이라는 새로운 함수 생성 model.get하여 this값으로 설정 model하고 내지 제 인수 세트 'z'.

의 좋은 설명은 bind찾을 수 있습니다 여기에 .


내가하지 않은 기능을 전달 했습니까? model인스턴스에는 get / get 함수가 있습니다.
doremi

아니, 당신이 당신의 의견을 쓰는 동안 추가 한 설명을 참조하십시오.
Louis

47
돈. 문서 ( chaijs.com/api/bdd/#throw ) 가 왜 바인드 사용법을 보여 주지 않습니까? 가장 일반적인 테스트 시나리오 인 것 같습니다. to.throw함수 내에서 특정 조건을 테스트하는 것입니다.이 경우 잘못된 상태 / 인수로 해당 함수를 호출해야합니다. (그 문제에 대해 .... chaijs.com의 딥 링크가 실제로 딥 링크가 아닌 이유는 무엇입니까?)
ericsoco

던져서는 안되는 일부 매개 변수를 전달하면 테스트는 여전히 통과입니다.
Alexandros Spyropoulos

6
비동기 기능에는이 기능이 작동하지 않습니다 (2017 년 9 월 기준) . github.com/chaijs/chai/issues/882#issuecomment-322131680 및 관련 토론을 참조하십시오 .
ChrisV

175

이 답변 에서 알 수 있듯이 다음 과 같이 익명 함수로 코드를 래핑 할 수도 있습니다.

expect(function(){
    model.get('z');
}).to.throw('Property does not exist in model schema.');

7
비동기 함수 호출에는 작동하지 않습니다. model.get이 promise를 반환하는 비동기라고 가정하십시오. 그러나 오류가 발생합니다. 위의 접근 방식을 시도하면 "완료"를 mocha에 알려야하므로 "타이밍"입니다. 동시에 expect(function(){ model.get('z'); }).to.throw('Property does not exist in model schema.').notify(done); 알림 방법이 없으므로 시도 할 수 없습니다 .
Anand N

@AnandN 문제를 이해하면 오류를 처리하기 위해 코드를 리팩터링 해야하는 것처럼 들립니다. 비동기 함수의 처리되지 않은 오류가 실제 앱에서도 문제가되지 않습니까?
twiz

2
답장을 보내 주셔서 감사합니다. 우리는 통합 환경에서 일하고 있으며 using 모듈은 예외를 포착합니다. 따라서 문제는 단위 테스트 사례를 실행하려고 할 때입니다. 마침내 우리는 아래의 접근법을 사용하여 작동 시켰습니다 catch (err) { expect(err).equal('Error message to be checked'); done(); }
Anand N

1
this호출 할 함수 내부 를 사용하는 경우를 제외하고 좋은 해결책 입니다. 그렇다면 .bind올바른 길입니다.
rabbitco

@AnandN 비동기 함수 호출은 던지지 않으며 , s를 거부 합니다. 나중에 참고하기 위해 chai-as-promised 는 이것을 아주 잘 처리합니다.
user5532169

85

그리고 이미 ES6 / ES2015를 사용하고 있다면 화살표 기능을 사용할 수도 있습니다. 기본적으로 일반적인 익명 함수를 사용하는 것과 동일하지만 더 짧습니다.

expect(() => model.get('z')).to.throw('Property does not exist in model schema.');

화살표 기능에 대한 자신의 주변 범위를 가지고 있기 때문에이에 문제가있을 수 있습니다this
에릭 Hodonsky

1
@Relic 예, 매우 그렇습니다. 이것은 화살표 기능의 큰 이점이 될 수도 있습니다. 화살표 함수 this는 생성 된 범위에서 '상속' 합니다. 종종 개체에 bind함수를 this수동으로 추가 할 필요가 없으므로 이점이 될 수 있습니다 .
Stijn de Witt

@StijndeWitt 이것은 장점이나 단점이 아니며 범위 제어와 의도적입니다. 실제로 부모 범위 를 사용 bind하고 항상 바인딩 하기위한 구문 설탕입니다 this. 이 의견에 대한 나의 의도는 독자들이 잠재적 인 구덩이 하락을 인식하도록하기위한 것이었다.
Eric Hodonsky

1
@Relic 예 동의합니다. 화살표 기능을 사용하는 것이 유리한 이유 일 수 있습니다.
Stijn de Witt

75

이 질문에는 Chai 어설 션 라이브러리를 언급하지 않은 질문을 포함하여 많은 중복 항목이 있습니다. 다음은 기본적으로 수집 된 것입니다.

어설 션은 즉시 평가하는 대신 함수를 호출해야합니다.

assert.throws(x.y.z);      
   // FAIL.  x.y.z throws an exception, which immediately exits the
   // enclosing block, so assert.throw() not called.
assert.throws(()=>x.y.z);  
   // assert.throw() is called with a function, which only throws
   // when assert.throw executes the function.
assert.throws(function () { x.y.z });   
   // if you cannot use ES6 at work
function badReference() { x.y.z }; assert.throws(badReference);  
   // for the verbose
assert.throws(()=>model.get(z));  
   // the specific example given.
homegrownAssertThrows(model.get, z);
   //  a style common in Python, but not in JavaScript

어설 션 라이브러리를 사용하여 특정 오류를 확인할 수 있습니다.

마디

  assert.throws(() => x.y.z);
  assert.throws(() => x.y.z, ReferenceError);
  assert.throws(() => x.y.z, ReferenceError, /is not defined/);
  assert.throws(() => x.y.z, /is not defined/);
  assert.doesNotThrow(() => 42);
  assert.throws(() => x.y.z, Error);
  assert.throws(() => model.get.z, /Property does not exist in model schema./)

할까요

  should.throws(() => x.y.z);
  should.throws(() => x.y.z, ReferenceError);
  should.throws(() => x.y.z, ReferenceError, /is not defined/);
  should.throws(() => x.y.z, /is not defined/);
  should.doesNotThrow(() => 42);
  should.throws(() => x.y.z, Error);
  should.throws(() => model.get.z, /Property does not exist in model schema./)

차이 기대

  expect(() => x.y.z).to.throw();
  expect(() => x.y.z).to.throw(ReferenceError);
  expect(() => x.y.z).to.throw(ReferenceError, /is not defined/);
  expect(() => x.y.z).to.throw(/is not defined/);
  expect(() => 42).not.to.throw();
  expect(() => x.y.z).to.throw(Error);
  expect(() => model.get.z).to.throw(/Property does not exist in model schema./);

테스트를 '탈출'하는 예외를 처리해야합니다.

it('should handle escaped errors', function () {
  try {
    expect(() => x.y.z).not.to.throw(RangeError);
  } catch (err) {
    expect(err).to.be.a(ReferenceError);
  }
});

처음에는 혼란스러워 보일 수 있습니다. 자전거를 타는 것처럼, 클릭하면 영원히 '클릭'합니다.


14

doc의 예 ...;)

this문맥에 의존하기 때문에 :

  • 함수가 .throw에 의해 호출되면 손실됩니다.
  • 이것이 무엇인지 알 방법이 없습니다

다음 옵션 중 하나를 사용해야합니다.

  • 메소드 또는 함수 호출을 다른 함수 안에
  • 바인드 컨텍스트

    // wrap the method or function call inside of another function
    expect(function () { cat.meow(); }).to.throw();  // Function expression
    expect(() => cat.meow()).to.throw();             // ES6 arrow function
    
    // bind the context
    expect(cat.meow.bind(cat)).to.throw();           // Bind

이것이 내가하는 방법입니다. 나는 ES6 구현이 지금까지 가장 읽을 수있는 하나입니다 것을 발견
relief.melone

1

기대하는 점을 () 수 있도록하는 데 도움이 또 다른 가능한 구현의 .bind () 솔루션보다 더 복잡하지만 하나가 제공하는 기능을 필요로 this적용 대상 기능에 컨텍스트, 당신은을 사용할 수 있습니다 call(), 예를 들어,

expect(function() {model.get.call(model, 'z');}).to.throw('...');


0

나는 그 주위에 좋은 방법을 찾았습니다.

// The test, BDD style
it ("unsupported site", () => {
    The.function(myFunc)
    .with.arguments({url:"https://www.ebay.com/"})
    .should.throw(/unsupported/);
});


// The function that does the magic: (lang:TypeScript)
export const The = {
    'function': (func:Function) => ({
        'with': ({
            'arguments': function (...args:any) {
                return () => func(...args);
            }
        })
    })
};

이전 버전보다 훨씬 읽기 쉽습니다.

it ("unsupported site", () => {
    const args = {url:"https://www.ebay.com/"}; //Arrange
    function check_unsupported_site() { myFunc(args) } //Act
    check_unsupported_site.should.throw(/unsupported/) //Assert
});
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.