2012-11-09 6 views
12

그래서 난 몇 가지 코드단위 테스트 코드입니다. ContinueWith()

Task.Factory.StartNew(() => this.listener.Start()).ContinueWith(
        (task) => 
         { 
          if (task.IsCompleted) 
          { 
           this.status = WorkerStatus.Started; 
           this.RaiseStatusChanged(); 
           this.LogInformationMessage("Worker Started."); 
          } 
         }); 

이 (namley this.listener.Start ()). 문제는 ContinueWith를 호출하기 전에 테스트가 실행을 완료한다는 것입니다. 내가 디버깅 할 때 코드를 통해 단계별로 추가 지연이 발생하여 문제가 발생합니다.

다른 어셈블리의 테스트 코드에서 어떻게하면 내 테스트가 어설 션에 도달하기 전에 코드가 실행되도록 할 수 있습니까?

난 그냥 Thread.Sleep을 사용할 수 있습니다 ...하지만이 일은 정말 해커처럼 보입니다.

나는 Thread.Join의 Task 버전을 찾고 있다고 생각한다.

+0

이 어떻게 마무리 않는

// ... test code to execute the code under test // run the tasks on the ThreadPool scheduler testSchedulers.ThreadPool.Start(); // assertion code can now run 
, 오류가? – maximpa

+0

오류 없음 ... listener.start가 조롱 받고 완료되었습니다. 디버그 모드에서 확인할 수 있습니다. 타이밍 문제입니다. –

+1

제가 동기화 문제라고 말하고 싶습니다. 호출하는 비동기 메서드에서 피드백을 받아야합니다. – maximpa

답변

2

는 다음과 같은 고려? 결과가 메소드 Foo() (반환 값, 공용 속성, 이벤트 등) 외부로 반환되지 않는 한 알 수 없습니다.

"coordinating the actions of threads for a predictable outcome"의 프로세스는 Synchronization이라고합니다. 귀하의 경우 가장 쉬운 솔루션

하나는 Task 클래스의 인스턴스를 반환 할 수 있습니다와는 Wait() 방법을 사용하십시오

var task = Task.Factory.StartNew(() => Method1()) 
    .ContinueWith(() => Method2()); 

ContinueWith()가 계속 만들기 때문에, 첫 번째 작업을 기다릴 필요가 없습니다 그 대상 작업이 완료 (MSDN) 비동기 적으로 실행 : 나는 D의 쉬운 아직 실용적인 방법이라고 생각하지 않습니다

task.Wait(); 
+1

나는 당신이 그의 문제를 오해하고 있다고 생각한다. 'ContinueWith' 메서드를 호출하기 전에 완료된 초기 작업이 호출 될 수 있기 때문에 그의 연속 작업은 분명히 실행되지 않습니다. – davenewza

+0

@davenewza'ContinueWith()'는 대상 태스크가 완료 될 때 비동기 적으로 실행되는 연속을 생성하기 때문에 첫 번째 태스크를 기다릴 필요가 없다. – maximpa

+3

내 테스트에서 태스크 인스턴스에 액세스하지 못하고 래퍼를 작성하지 않을 것이다. 단지 그 목적을 위해 (하지만 아마도). –

1

처리가 끝났을 때 알림을받을 수있는 방법이 있으면 (해당 StatusChanged 이벤트에 대한 처리기를 추가 할 수 있습니까?) ManualResetEvent를 사용하고 적절한 시간 초과로 기다립니다. 시간 초과가 만료 된 경우 테스트에 실패하고, 그렇지 않으면 계속해서 주장을 수행하십시오.

예.

+0

나는 상자 밖의 생각을 좋아합니다. 하지만 일반적인 문제로 나를 도와주지 않습니다! –

+0

그가 이해하도록 도와주세요. 당신은 Thread.Sleep을 대안으로 생각하고있었습니다. 이런 식으로 작동하지만, 알림을 받으면 처리가 끝나는대로 계속 진행됩니다. Thread.Join에 해당하는 것은 Task.Wait이지만 테스트중인 메서드에 작업을 노출시키지 않는다고 말한 것입니다. – fsimonazzi

+0

. 어쩌면 나는 불가능한 상황을 만들었을 것이다. 어떤 경우에는 내가 대답으로 표시됩니다;) ... 일반 대답은 지금 내가 할 일을 할 수 있도록 작업 물건 주위에 일종의 래퍼를 작성하는 것 같습니다. 나는 정말로 내가 바퀴를 재발 명하려고하지 않는다는 것을 확인하고있다. –

0

계속 작업은 ContinueWith() 호출 전에 완료되었는지 여부에 관계없이 계속 실행됩니다. 나는 이것을 다음과 같이 두 번 확인했다 :

// Task immediately exits 
var task = Task.Factory.StartNew(() => { }); 

Thread.Sleep(100); 

// Continuation on already-completed task 
task.ContinueWith(t => { MessageBox.Show("!"); }); 

더 디버그. 어쩌면 당신의 임무는 실패 할 것입니다.

public class SomeClass 
{ 
    public void Foo() 
    { 
     var a = new Random().Next(); 
    } 
} 

public class MyUnitTest 
{ 
    public void MyTestMethod() 
    { 
     var target = new SomeClass();   
     target.Foo(); // What to assert, what is the result?.. 
    } 
} 

이 값이 a 어떻게 할당됩니다

+0

이것은 내가 묻고있는 것이 아니다. 내가 didnt하는 you''re가 당신이 afterthought로서 continuation를 더할 수 있었다는 것을 깨달을지라도 당신이 이것을 할 수 있다고 흥미를 일으키는 것! 내가 조롱하고 아무 것도하지 않기 때문에 내 작업이 실패하지 않을 수 있습니다 ... 사실 당신과 같은 일을 Moq 프록시를 통해 실행하고 있습니다. 또한 thread.sleep 정확히 내가 뭘하려고 피하려고합니다. 문제는 tere은 실행중인 작업과 실행을 계속하는 사이의 지연이며이 지연은 나머지 테스트 수행 시간보다 길다는 것입니다. 명시 적 스레드로 수행 할 수있는 작업과 동등한 작업을 수행하고 마무리 작업을 기다렸다가 다시 참가 시키려합니다. –

2

이러는거야. 지금 당장 같은 문제에 봉착하고 Thread.Sleep (X)는 문제를 해결하는 가장 간단한 방법입니다.

내가 생각했던 유일한 해결책은 테스트에서 모의 ​​할 수있는 인터페이스 뒤에 Task.Factory.StartNew() 호출을 숨기는 것이므로 테스트 시나리오에서 작업의 실제 실행을 완전히 제거합니다. 인터페이스 메소드가 호출 될 것을 기대한다.

public interface ITaskWrapper 
{ 
    void TaskMethod(); 
} 

그리고 당신의 구체적인 구현 : 예를 들어

public class MyTask : ITaskWrapper 
{ 
    public void TaskMethod() 
    { 
     Task.Factory.StartNew(() => DoSomeWork()); 
    } 
} 

그럼 그냥 모의 테스트 방법 ITaskWrapperTaskMethod에 기대를 설정 호출되고.

+0

그래,이게 내가 문제를 처리하는 방법 이었어. 어떤 의미있는 테스트를 가능하게 할 수있는 유일한 방법 이었습니까? –

+0

이것은 또한 저자에 의해 여기에서 사용 된 방법이다 : http://www.codeproject.com/Tips/706553/Mocking-Task-Factory-StartNew – Appulus

0

Reactive Extensions를 사용하는 테스트중인 코드에서 비동기 프로세스를 처리 할 때 한 가지 방법은 TestScheduler를 사용하는 것입니다. TestScheduler는 시간이 지나면 앞으로 움직일 수 있고, 모든 shceduled 작업 등이 빠져 나갈 수 있습니다. 따라서 테스트중인 코드는 TestScheduler 인스턴스를 제공하는 IScheduler를 사용할 수 있습니다. 그런 다음 실제로 잠자고, 기다리거나 동기화 할 필요없이 테스트가 시간을 조작 할 수 있습니다. 이 접근 방식의 개선점은 Lee Campbell's ISchedulerProvider입니다.

코드에서 Task.Factory.StartNew 대신 Observable.Start를 사용하면 단위 테스트에서 TestScheduler를 사용하여 예약 된 모든 작업을 수행 할 수 있습니다.

//Task.Factory.StartNew(() => DoSomething()) 
// .ContinueWith(t => DoSomethingElse()) 
Observable.Start(() => DoSomething(), schedulerProvider.ThreadPool) 
      .ToTask() 
      .ContinueWith(t => DoSomethingElse()) 

및 단위 테스트에서 :

예를 들어, 테스트중인 코드는 다음과 같이 보일 수

+0

이게 재미있어 보인다. 내가이 문제를 다시 만났을 때 나는 기억할 것이다. DI는 비슷한 것을했습니다. –

+0

이 답변에는 비슷한 방법이 있습니다 : http://stackoverflow.com/a/17844870/114200 작업 스케줄러를 사용하고 사용자의 스케줄러를 구현하는 것을 제외하고는. 위의 방법을 선호하지만, Rx를 아직 사용하고 있지 않다면 Task Scheduler 접근법이 중요 할 수도 있습니다. –