2010-11-10 7 views
354

Próbuję napisać test dla Jasmine Test Framework, który oczekuje błędu. W tej chwili używam Jasmine Node.js integration from GitHub.Jak napisać test, który oczekuje, że błąd zostanie rzucony w Jasmine?

W moim modułu Node Mam następujący kod:

throw new Error("Parsing is not possible"); 

Teraz próbuję napisać test, który spodziewa się ten błąd:

describe('my suite...', function() { 
    [..] 
    it('should not parse foo', function() { 
    [..] 
     expect(parser.parse(raw)).toThrow(new Error("Parsing is not possible")); 
    }); 
}); 

Próbowałem też Error() i kilka innych wariantów i tylko nie mogę wymyślić, jak to działa.

+3

przekazywać argumenty do funkcji testowane, bez użycia anonimową funkcję, spróbuj 'Function.bind': http://stackoverflow.com/ a/13233194/294855 –

Odpowiedz

628

ty powinny być przechodzącą funkcji do wywołania expect(...). Kod masz tutaj:

// incorrect: 
expect(parser.parse(raw)).toThrow(new Error("Parsing is not possible")); 

stara się rzeczywiście wezwanieparser.parse(raw) próbując przekazać wynik do expect(...),

Spróbuj użyć anonimową funkcję zamiast:

expect(function(){ parser.parse(raw); }).toThrow(new Error("Parsing is not possible")); 
+21

Jeśli nie musisz podawać argumentów, możesz po prostu przekazać tę funkcję, aby oczekiwać: 'expect (parser.parse) .toThrow (...)' – SubmittedDenied

+47

Pomocna wskazówka: Możesz po prostu wywołać 'expect (blah) .toThrow() '. Żadne argumenty nie oznaczają, że sprawdzasz, czy w ogóle to rzuca. Nie jest wymagane dopasowanie do łańcucha. Zobacz także: http://stackoverflow.com/a/9525172/1804678 – Jess

+1

Moim zdaniem, jest bardziej oczywiste, co do zamiaru testu podczas zawijania w anonimowej funkcji. Ponadto, pozostaje on stały wśród wszystkich testów, gdy na przykład musisz przekazać parametry do funkcji docelowej, aby ją rzucić. – Beez

55

Używasz:

expect(fn).toThrow(e) 

Ale jeśli musisz spojrzeć na komentarzu funkcyjnego (oczekiwany jest ciąg znaków):

294 /** 
295 * Matcher that checks that the expected exception was thrown by the actual. 
296 * 
297 * @param {String} expected 
298 */ 
299 jasmine.Matchers.prototype.toThrow = function(expected) { 

Przypuszczam powinieneś napisać to tak (przy użyciu funkcji lambda - anonimowej):

expect(function() { parser.parse(raw); }).toThrow("Parsing is not possible"); 

Zostało to potwierdzone w następującym przykładzie:

expect(function() {throw new Error("Parsing is not possible")}).toThrow("Parsing is not possible"); 

Douglas Crockforda zaleca to podejście, zamiast używać "throw new Error()" (sposób prototypowania):

throw { 
    name: "Error", 
    message: "Parsing is not possible" 
} 
+3

Właściwie patrząc na kod do Throw z przyjemnością wybierze obiekt wyjątku/lub ciąg znaków. Sprawdź połączenia, które robi na przykład na oczekiwaną wiadomość. –

+1

To szwy, aby umożliwić ciąg jako efekt uboczny ciąg bez właściwości wiadomości – mpapis

+1

Dziękuję bardzo, że działało. Wciąż akceptowałem odpowiedź Pete'a, ponieważ jego odpowiedź uświadomiła mi wyraźnie, że muszę używać lambda. Nadal +1 :-) Dzięki! – echox

22

I zamień Jasmine's toThrow matcher na następujące, co pozwala dopasować właściwość nazwy wyjątku lub jego właściwość komunikatu. Dla mnie to sprawia, że ​​testy łatwiej napisać i mniej kruche, jak mogę zrobić następujące:

throw { 
    name: "NoActionProvided", 
    message: "Please specify an 'action' property when configuring the action map." 
} 

a następnie przetestować z poniższym:

expect (function() { 
    .. do something 
}).toThrow ("NoActionProvided"); 

To pozwala mi dostosować komunikat wyjątku później bez zerwania testy, gdy ważne jest, aby rzucił oczekiwany typ wyjątku.

Jest to zamiennik toThrow że pozwala na to:

jasmine.Matchers.prototype.toThrow = function(expected) { 
    var result = false; 
    var exception; 
    if (typeof this.actual != 'function') { 
    throw new Error('Actual is not a function'); 
    } 
    try { 
    this.actual(); 
    } catch (e) { 
    exception = e; 
    } 
    if (exception) { 
     result = (expected === jasmine.undefined || this.env.equals_(exception.message || exception, expected.message || expected) || this.env.equals_(exception.name, expected)); 
    } 

    var not = this.isNot ? "not " : ""; 

    this.message = function() { 
    if (exception && (expected === jasmine.undefined || !this.env.equals_(exception.message || exception, expected.message || expected))) { 
     return ["Expected function " + not + "to throw", expected ? expected.name || expected.message || expected : " an exception", ", but it threw", exception.name || exception.message || exception].join(' '); 
    } else { 
     return "Expected function to throw an exception."; 
    } 
    }; 

    return result; 
}; 
+3

Ładne podejście, ale to {nazwa: "...", wiadomość: "..."} właściwy obiekt błędu w JavaScript? – Marc

+1

Przyjemny komentarz @Marc. Masz rację, właściwość nazwy nie jest standardowa. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error, ale czy to źle? – Jess

+3

@Jake! Znalazłem lepszy sposób !!!! Możesz po prostu wywołać 'expect (blah) .toThrow()'. Żadne argumenty nie oznaczają, że sprawdzasz, czy w ogóle to rzuca. Nie jest wymagane dopasowanie do łańcucha. Zobacz także: http://stackoverflow.com/a/9525172/1804678 – Jess

5

Dla miłośników coffeescript

expect(=> someMethodCall(arg1, arg2)).toThrow() 
7

wiem, że jest więcej kodu, ale można też zrobić:

try 
    do something 
    @fail Error("should send a Exception") 
catch e 
    expect(e.name).toBe "BLA_ERROR" 
    expect(e.message).toBe 'Message' 
+0

Ja lubię aspekt "samo-dokumentowania" tego ... sprawia, że ​​oczywiste jest, że jesteś jednostką testującą stan błędu. – JRulle

19

Bardziej eleganckim rozwiązaniem niż tworzenie anonimowej funkcji, której jedynym celem jest objęcie innego, jest użycie es2'sFunkcja. Funkcja wiążąca tworzy nową funkcję, która po wywołaniu ma ustawione słowo kluczowe na podaną wartość, z podaną sekwencją argumentów poprzedzającą wszelkie podawane podczas wywoływania nowej funkcji.

Zamiast:

expect(function() { parser.parse(raw, config); }).toThrow("Parsing is not possible");

Rozważmy:

expect(parser.parse.bind(parser, raw, config)).toThrow("Parsing is not possible");

Składnia wiążą pozwala testować funkcje z różnymi wartościami this, a moim zdaniem sprawia, że ​​test był bardziej czytelny. Zobacz także: https://stackoverflow.com/a/13233194/1248889

6

Jak wspomniano wcześniej, funkcja musi zostać przekazany do toThrow, ponieważ jest to funkcja opisująca jesteś w teście: „Spodziewam się tej funkcji, aby rzucić x”

expect(() => parser.parse(raw)) 
    .toThrow(new Error('Parsing is not possible')); 

Jeśli używając Jasmine-Matchers możesz również użyć jednej z poniższych opcji, gdy odpowiadają one sytuacji;

// I just want to know that an error was 
// thrown and nothing more about it 
expect(() => parser.parse(raw)) 
    .toThrowAnyError(); 

lub

// I just want to know that an error of 
// a given type was thrown and nothing more 
expect(() => parser.parse(raw)) 
    .toThrowErrorOfType(TypeError); 
+2

To "spodziewać się (foo) .toThrowError (TypeError);" w Jasmine 2.5: https://jasmine.github.io/2.5/introduction –

0

Dla każdego, kto jeszcze może być w obliczu tego problemu, dla mnie pisał rozwiązanie nie działa i jest utrzymywana na rzucanie ten błąd: Error: Expected function to throw an exception. Później zrozumiał, że funkcja spodziewałem się, że błąd zostanie zgłoszony jako funkcja asynchroniczna i spodziewałem się, że obietnica zostanie odrzucona, a następnie zgłoszę błąd i to właśnie robiłem w moim kodzie:

throw new Error('REQUEST ID NOT FOUND'); 

i to, co zrobiłem w moim teście i to działało:

it('Test should throw error if request not found', willResolve(() => { 
     const promise = service.getRequestStatus('some-id'); 
       return expectToReject(promise).then((err) => { 
        expect(err.message).toEqual('REQUEST NOT FOUND'); 
       }); 
      })); 
Powiązane problemy