[英]How to test an exception from a future
繼續昨天的問題,我將如何測試異步方法拋出異常。
main(){
test( "test2", () async {
expect( await throws(), throwsException);
});
}
Future throws () async {
throw new FormatException("hello");
}
使用 try-catch
最可靠的方法是使用try-catch塊來顯式捕獲異常並確保方法已完成運行。
try {
await methodWhichThrows();
fail("exception not thrown");
} catch (e) {
expect(e, new isInstanceOf<...>());
// more expect statements can go here
}
這種方法還有一個優點,即可以對異常值執行額外的檢查。
Expect with throwsA 僅用作最后一條語句
僅當它是測試中的最后一個語句時,單獨使用expect才有效。 有過當方法會拋出異常,所以不可能有競爭狀態與報表(包括后續調用期望的那樣),如果他們假定異常已經拋出無法控制。
expect(methodWhichThrows(), throwsA(new isInstanceOf<...>())); // unreliable unless last
它可以使用,但您必須非常小心地記住它在哪些情況下有效,哪些情況下無效。 因此,堅持使用try-catch方法比針對不同情況使用不同方法更安全。
示范
以下完整示例演示了競爭條件對兩種方法的影響:
import 'dart:async';
import 'package:test/test.dart';
//----------------------------------------------------------------
/// This approach to expecting exceptions is reliable.
///
Future reliableApproach(int luck) async {
expect(await setValueAndReturnsHalf(42), equals(21));
expect(state, equals(Evenness.isEven));
try {
await setValueAndReturnsHalf(3);
fail("exception not thrown");
} catch (e) {
expect(e, new isInstanceOf<ArgumentError>());
}
// Expect value to be odd after execption is thrown.
await shortDelay(luck); // in my experience there's no such thing called luck
expect(state, equals(Evenness.isOdd));
}
//----------------------------------------------------------------
/// This approach to expecting exceptions is unreliable.
///
Future unreliableApproach(int luck) async {
expect(await setValueAndReturnsHalf(42), equals(21));
expect(state, equals(Evenness.isEven));
expect(setValueAndReturnsHalf(3), throwsA(new isInstanceOf<ArgumentError>()));
// Expect value to be odd after execption is thrown.
await shortDelay(luck); // luck determines if the race condition is triggered
expect(state, equals(Evenness.isOdd));
}
//----------------------------------------------------------------
enum Evenness { isEven, isOdd, inLimbo }
int value = 0;
Evenness state = Evenness.isEven;
/// Sets the [value] and [state].
///
/// If the [newValue] is even, [state] is set to [Evenness.isEven] and half of it
/// is returned as the Future's value.
///
/// If the [newValue] is odd, [state] is set to [Evenness.isOdd] and an exception
/// is thrown.
///
/// To simulate race conditions, this method takes 2 seconds before it starts
/// processing and 4 seconds to succeed or throw an exception. While it is
/// processing, the [state] is set to [Evenness.inLimbo].
///
Future<int> setValueAndReturnsHalf(int newValue) async {
await shortDelay(2);
state = Evenness.inLimbo;
await shortDelay(2);
value = newValue;
if (newValue % 2 != 0) {
state = Evenness.isOdd;
throw new ArgumentError.value(newValue, "value", "is not an even number");
} else {
state = Evenness.isEven;
return value ~/ 2;
}
}
/// Delays used to simulate processing and race conditions.
///
Future shortDelay(int seconds) {
var c = new Completer();
new Timer(new Duration(seconds: seconds), () => c.complete());
return c.future;
}
/// Examples of the reliable and unreliable approaches.
///
void main() {
test("Correct operation when exception is not thrown", () async {
expect(await setValueAndReturnsHalf(42), equals(21));
expect(value, equals(42));
});
group("Reliable approach:", () {
test("works when there is bad luck", () async {
// 1 second = bad luck, future returning function not started processing yet
await reliableApproach(1);
});
test("works when there is more bad luck", () async {
// 3 second = bad luck, future returning function still processing
await reliableApproach(3);
});
test("works when there is good luck", () async {
// 5 seconds = good luck, future returning function definitely finished
await reliableApproach(5);
});
});
group("Unreliable approach:", () {
test("race condition encountered by bad luck", () async {
// 1 second = bad luck, future returning function not started processing yet
await unreliableApproach(1);
});
test("race condition encountered by more bad luck", () async {
// 3 second = bad luck, future returning function still processing
await unreliableApproach(3);
});
test("race condition avoided by good luck", () async {
// 5 seconds = good luck, future returning function definitely finished
await unreliableApproach(5);
});
});
}
它是這樣工作的:
import 'package:test/test.dart';
import 'dart:async';
void main() {
test( "test2", () { // with or without `async`
expect(throws(), throwsA(const TypeMatcher<FormatException>()));
});
}
Future throws () async {
throw new FormatException("hello");
}
基本上只是刪除await
。 無論成功還是失敗,測試框架都可以處理期貨。
官方文檔有很好的例子,通過將expectLater
與throwsA
使用,如下例所示。
void functionThatThrows() => throw SomeException();
void functionWithArgument(bool shouldThrow) {
if (shouldThrow) {
throw SomeException();
}
}
Future<void> asyncFunctionThatThrows() async => throw SomeException();
expect(functionThatThrows, throwsA(isA<SomeException>()));
expect(() => functionWithArgument(true), throwsA(isA<SomeException>()));
var future = asyncFunctionThatThrows();
await expectLater(future, throwsA(isA<SomeException>()));
await expectLater(
asyncFunctionThatThrows, throwsA(isA<SomeException>()));
有多種方法可以測試來自 Future 的錯誤。 如果“無異步拋出”方法拋出一些異常,Gunter 的回答將起作用。 下面的示例將處理來自未來方法的異常。
import 'package:test/test.dart';
import 'dart:async';
void main() {
test("test with Zone", () {
runZoned(() {
throws();
}, onError: expectAsync((e, s) {
expect(e, new isInstanceOf<FormatException>());
}));
});
test('test with future catch error', () {
throws().catchError(expectAsync((e) {
expect(e, new isInstanceOf<FormatException>());
}));
});
}
Future throws() async{
Completer completer = new Completer();
completer.complete(new Future(() => throw new FormatException("hello")));
return completer.future;
}
最簡單和最短的答案是:
expect(throws(), throwsException)
測試異常/錯誤類型:
expect(throws(), throwsA(predicate((e) => e is MyException)));
經過太多的嘗試和錯誤,我發現這個可以按預期工作:
test('fetch SHOULD throw exception WHEN api fail with exception', () {
when(clientMock.get(uri)).thenAnswer((_) async => throw Exception());
expect(() => sut.fetch(), throwsA(isInstanceOf<Exception>()));
});
當任何其他答案時我都不滿意; 太冗長了。
所以簡短的版本是:
test('ArgumentError is thrown when throwIt is true', () {
expectLater(() => myAsyncFunctionThatTakesAnArgument(throwIt: true), throwsA(isA<ArgumentError>()));
});
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.