2016-06-16 5 views
1

클래스의 변수를 변경하면 단위 테스트를하려고하는 이벤트가 비동기 적으로 실행됩니다. 단위 테스트 비동기 이벤트

class A { 

    Action<int> OnIntChange; 

    private int _a; 
    public int a { 
     set { 
      OnIntChange(value); 
     } 
    } 
} 

class B { 

    RaiseAsyncEvent(int value) { 
     Task.Factory.StartNew(() => {c = value;}); 
    } 

    int c; 
} 

는 이전에 모든 동기, 그래서 아주 쉽게 내가 할 수 단위 테스트 :

B b = new B(); 
A.OnIntChange += b.RaiseAsyncEvent;  
A.a = 10; 
Assert.AreEqual(10, A.b.c); 

그러나이 지금 실패합니다. ManualResetEvents을 사용할 수 없습니다. 왜냐하면이 이벤트를 직접 제기하지 않기 때문에 코드 재구성 없이는 불가능합니다. 내 유일한 옵션은 A.a = 10을 호출 한 후 System.Threading.Thread.Sleep() 호출을 추가하는 것이지만 다른 옵션을 찾고 있습니다. 제안?

~~~~~~~~~~ 편집 ~~~~~~~~

내가 찾고 있어요 하나 개의 잠재적 인 솔루션이이 생성 작업과 그에 필드를 설정을 반환 RaiseAsyncEvent하는 것입니다

태스크. 뭔가 같은 :

class B { 

    Task task; 

    RaiseAsyncEvent(int value) { 
     task = Task.Factory.StartNew(() => {c = value;}); 
    } 

    int c; 
} 


B b = new B(); 
A.OnIntChange += b.RaiseAsyncEvent; 
b.task.Wait(); 
A.a = 10; 
Assert.AreEqual(10, A.b.c); 

~~~~~~~~~~ 나는 다음이 변경되었다 B를 해결하기 위해 일을 결국 무엇 또한 편집 ~~~~~~~~~~

:

class B { 

    bool RunAsync = true; 

    RaiseAsyncEvent(int value) { 
     Task task = Task.Factory.StartNew(() => {c = value;}); 
     if(!RunAsync) { task.Wait();} 
    } 

    int c; 
} 

과에 테스트를 변경 :

B b = new B(); 
b.RunAsync = false; 
A.OnIntChange += b.RaiseAsyncEvent;  
A.a = 10; 
Assert.AreEqual(10, A.b.c); 

답변

2

당신은 B의 생성자의 사용자 정의 버전에서 를 전달할 수 있습니다 TaskScheduler은 단위 테스트에서 모든 작업이 완료 될 때까지 대기 할 수 있습니다.

class B { 
    public B(TaskScheduler taskScheduler) 
    { 
     _taskScheduler = taskScheduler; 
    } 


    public B(): this(TaskScheduler.Default) 
    { 
    } 


    public void RaiseAsyncEvent(int value) 
    { 
     Task.Factory.StartNew(() => {c = value;}, CancellationToken.None, 
     TaskCreationOptions.DenyChildAttach, _taskScheduler); 
    } 

    TaskScheduler _taskScheduler; 

} 

ConcurrentExclusiveSchedulerPair은 우리가 추구하는 완성을 기다리는 기능이 있습니다.

// Unit test 

var schedulerPair = new ConcurrentExclusiveSchedulerPair(); 

B b = new B(schedulerPair.ConcurrentScheduler); 
A.OnIntChange += b.RaiseAsyncEvent;  
A.a = 10; 

schedulerPair.Complete(); 
schedulerPair.Completion.Wait(); 

Assert.AreEqual(10, A.b.c); 

개 이상의 비동기 작업을 감시해야 할 때 그것은 더 고급 시나리오를 확장 :

B b1 = new B(schedulerPair.ConcurrentScheduler); 
B b2 = new B(schedulerPair.ConcurrentScheduler); 
B b3 = new B(schedulerPair.ConcurrentScheduler); 

A.OnIntChange += b1.RaiseAsyncEvent;  
A.OnIntChange += b2.RaiseAsyncEvent;  
A.OnIntChange += b3.RaiseAsyncEvent;  
A.a = 10; 

schedulerPair.Complete(); 
schedulerPair.Completion.Wait(); 

Assert.AreEqual(10, b1.c); 
Assert.AreEqual(10, b2.c); 
Assert.AreEqual(10, b3.c); 

편집 : TaskScheduler 인스턴스가이 경우에, 정적 속성을 사용하여 공유 할 수 있습니다 B 생성자 서명이 변경되지 않습니다.

static class EventScheduler 
{ 

    public static TaskScheduler TaskScheduler 
    { 
     get {return _taskScheduler; } 
     set {_taskScheduler = value; } 
    } 

    static TaskScheduler _taskScheduler = TaskScheduler.Default; 

    public static Task RunAsync(Action<T> action) 
    { 
     return Task.Factory.StartNew(() => {c = value;}, 
     CancellationToken.None, TaskCreationOptions.DenyChildAttach, 
     _taskScheduler); 
    } 
} 

class B 
{ 
    public void RaiseAsyncEvent(int value) 
    { 
     EventScheduler.RunAsync(()=>{c = value;}); 
    } 
} 

// Unit test 

var schedulerPair = new ConcurrentExclusiveSchedulerPair(); 
EventScheduler.TaskScheduler = schedulerPair.ConcurrentScheduler; 

B b = new B(); 
A.OnIntChange += b.RaiseAsyncEvent;  
A.a = 10; 

schedulerPair.Complete(); 
schedulerPair.Completion.Wait(); 

Assert.AreEqual(10, A.b.c); 
+0

나는이 대답을 좋아하고 그것을 받아 들인 대답으로 표시하고있다. 나는 내가 묻는 방식으로 내 문제를 절대적으로 돌봐야 만했다. 그러나 내가 수행 한 결과 B에서 "RunAsync"bool 필드를 추가하여 동기식으로 작업을 실행하는 것입니다. if (! RunAsync) { task.wait(); } RaiseAsyncEvent 내에 있고 b.RunAsync = false로 설정하십시오. TestInitialize에서. 가장 예쁜 것은 아니지만 정말 복잡한 문제 일 수있는 간단한 수정 사항입니다. –

+0

생성자 시그니처를 변경하지 않으려면 모든 이벤트에서 공유하는 TaskScheduler의 정적 인스턴스 (단독 개체)를 고려하십시오. 그런 다음 모든 객체에 RunAsync 속성을 설정하는 대신 ConcurrentScheduler를 정적 속성에 할당하면됩니다. – alexm

+0

내가 작업하고있는 코드는 여기에 표시 할 수있는 것보다 훨씬 복잡합니다. 실제로 이벤트를 전달하는 내 정적 CallbackManager를 구현 중이며 몇 가지 요인에 따라 비동기 또는 동기식으로 실행되는 새 태스크를 생성합니다. 핸들을 저장하고 리턴 논리를 처리합니다. 내가 추가하고있는 부울은 비동기 논리를 우회하여 모든 단위 테스트를 통과시키는 시작부터 동기화되도록 강제합니다. 관리자는 정적이기 때문에 테스트 시작시 RunAsync bool에 대한 단일 변경 이상은 필요하지 않으며 정상적으로 실행됩니다. –