2016-07-07 5 views
2

이의 내가 이런 식으로 F 번호 비동기를 지원하기 위해 xUnit의의 Assert.Throws을 확장하고 싶은 말은하자 unit -> Async<obj>으로 추측됩니다. 이것은 발신자에게 불필요하게 제한적입니다. unit -> Async<'a>이어야합니다.형식 유추

let AsyncThrows<'TException when 'TException :> exn> (asyncTask:unit->Async<'a>) 

이 작동 여전히 비밀 경고 Async<obj>로 컴파일하지 않습니다는 ("... ... 표시보다 일반적으로 코드를 일으키는") : 나는 다음과 같은 노력했다.

let AsyncThrows<'TException, 'a when 'TException :> exn> (asyncTask:unit->Async<'a>) 

이것은 작동하지만 호출자가 비동기 함수의 반환 유형을 명시 적으로 제공하도록합니다.

Assert.AsyncThrows<InvalidOperationException, string>(fun() -> async { return "" }) 

비동기 함수가 아닌 예외 유형 만 제공하면됩니다.

(참고 : 실제로 사용하는 경우 비동기가 아니라 다른 유사한 계산식이 사용되며, 비동기를 사용하여 예제로 사용됩니다.)

답변

4

최소 복잡한 옵션은 두 번째 제네릭 인수 (밑줄 일명) 기호 "나를 사랑하는 컴파일러 그림이를 기쁘게"공급하는 것입니다 :

AsyncThrows<InvalidOperationException, _>(fun() -> async { return "" }) 

또 다른 옵션은 입력 매개 변수를 제공하는 것 인터페이스를 반환하여 이 방법은, 두 번째 인수는 추론 할 수 있습니다

type ExnType<'e when 'e :> exn> = | ExnType 

let exnType<'e when 'e :> exn> : ExnType<'e> = ExnType 

let AsyncThrows<'e, 'a when 'e :> exn> (_ :ExnType<'e>) (fn: unit -> Async<'a>) = 
    // Implementation here 

// Usage: 
AsyncThrows exnType<NotImplementedException> (fun() -> async { return "" }) 
:

type IAsyncAssert<'e when 'e :> exn> = 
    abstract member When<'a> : (unit -> Async<'a>) -> unit 


let AsyncThrows<'e when 'e :> exn>() = 
    { new IAsyncAssert<'e> with 
     override x.When<'a> (fn: unit -> Async<'a>) = 
      // Implementation goes here 
    } 

// Usage: 
AsyncThrows<NotImplementedException>().When(fun() -> async { return "" }) 

는 또 다른 (더 많은 기능) 옵션은 단지 일반적인 인수를 추론 할 수있는 권한 유형의 "더미 객체"를 제공하는 것

힌트는 다음과 같습니다. F # 비동기 값은 C# Tasks와 달리 즉시 평가되지 않지만 다른 비동기의 일부로 사용되거나 직접 Async.Start 등으로 사용될 때만 평가됩니다. 따라서 람다 표현식없이 할 수 있습니다.

... 
let AsyncThrows<'e, 'a when 'e :> exn> (_ :ExnType<'e>) (a: Async<'a>) = 
    // Implementation here 
    let! r = a 
    ... 

// Usage: 
AsyncThrows exnType<NotImplementedException> (async { return "" })