다음을 통해 공유


관찰 가능한 시퀀스 테스트 및 디버깅

Rx 애플리케이션 테스트

오랜 기간 동안 값을 게시하는 관찰 가능한 시퀀스가 있는 경우 실시간으로 테스트하는 것이 늘어나게 될 수 있습니다. 반응형 확장 라이브러리는 실제로 시간이 경과할 때까지 기다리지 않고 이러한 종류의 시간 종속 코드를 테스트하는 데 도움이 되는 TestScheduler 형식을 제공합니다. TestScheduler는 VirtualScheduler를 상속하며 에뮬레이트된 시간에 시퀀스를 만들고 게시하고 구독할 수 있습니다. 예를 들어 올바른 배율을 유지하면서 2분 실행으로 완료하는 데 5일이 걸리는 게시를 압축할 수 있습니다. 또한 실제로 과거에 발생한 시퀀스(예: 전년도의 주식 틱 시퀀스)를 사용하여 실시간으로 새 값을 푸시하는 것처럼 계산하거나 구독할 수 있습니다.

팩터리 메서드 Start는 큐가 비어 있는 때까지 예약된 모든 작업을 실행하거나, 큐에 대기 중인 작업이 지정된 시간까지만 실행되도록 시간을 지정할 수 있습니다.

다음 예제에서는 지정된 OnNext 알림을 사용하여 관찰 가능한 핫 시퀀스를 만듭니다. 그런 다음, 테스트 스케줄러를 시작하고 관찰 가능한 핫 시퀀스를 구독하고 삭제할 시기를 지정합니다. Start 메서드는 목록의 모든 알림을 기록하는 Messages 속성을 포함하는 ITestableObserver의 instance 반환합니다.

시퀀스가 완료된 후 ReactiveAssert.AreElementEqual 메서드를 사용하여 Messages 속성을 비교하고 예상 값 목록과 함께 두 값이 동일한지 확인합니다(항목 수가 같고 항목이 같은 순서로 표시됨). 이렇게 하면 예상되는 알림을 실제로 수신했는지 확인할 수 있습니다. 이 예제에서는 150에서만 구독을 시작하므로 값 abc이 누락됩니다. 그러나 지금까지 400에서 받은 값을 비교할 때 시퀀스를 구독한 후 게시된 모든 값을 실제로 받은 것을 알 수 있습니다. 또한 알림이 OnCompleted 500에서 적시에 발생했는지도 확인합니다. 또한 구독 정보는 CreateHotObservable 메서드에서 반환된 ITestableObservable 형식으로 캡처됩니다.

동일한 방식으로 ReactiveAssert.AreElementsEqual을 사용하여 구독이 실제로 예상 시간에 발생했는지 확인할 수 있습니다.

using System;
using System.Reactive;
using System.Reactive.Linq;
using Microsoft.Reactive.Testing;

class Program : ReactiveTest
{
    static void Main(string[] args)
    {
        var scheduler = new TestScheduler();

        var input = scheduler.CreateHotObservable(
            OnNext(100, "abc"),
            OnNext(200, "def"),
            OnNext(250, "ghi"),
            OnNext(300, "pqr"),
            OnNext(450, "xyz"),
            OnCompleted<string>(500)
            );

        var results = scheduler.Start(
            () => input.Buffer(() => input.Throttle(TimeSpan.FromTicks(100), scheduler))
                       .Select(b => string.Join(",", b)),
            created: 50,
            subscribed: 150,
            disposed: 600);

        ReactiveAssert.AreElementsEqual(results.Messages, new Recorded<Notification<string>>[] {
                OnNext(400, "def,ghi,pqr"),
                OnNext(500, "xyz"),
                OnCompleted<string>(500)
            });

        ReactiveAssert.AreElementsEqual(input.Subscriptions, new Subscription[] {
                Subscribe(150, 500),
                Subscribe(150, 400),
                Subscribe(400, 500)
            });
    }
}

Rx 애플리케이션 디버깅

Do 연산자를 사용하여 Rx 애플리케이션을 디버그할 수 있습니다. Do 연산자를 사용하면 관찰 가능한 시퀀스의 각 항목(예: 항목 인쇄 또는 로그 등)에 대해 수행할 다양한 작업을 지정할 수 있습니다. 이는 많은 연산자를 연결하고 각 수준에서 생성되는 값을 알고 싶을 때 특히 유용합니다.

다음 예제에서는 매초 정수 생성 버퍼 예제를 다시 사용하면서 각각 5개의 항목을 보유할 수 있는 버퍼에 넣습니다. LINQ 연산자를 사용하여 관찰 가능한 시퀀스 쿼리 항목의 원래 예제에서는 버퍼가 가득 차서 비우기 전에 최종 Observable(IList<>) 시퀀스만 구독합니다. 그러나 이 예제에서는 Do 연산자를 사용하여 원래 시퀀스(1초마다 정수)로 푸시될 때 값을 출력합니다. 버퍼가 가득 차면 Do 연산자를 사용하여 상태 인쇄한 후 이 모든 것을 관찰자가 구독할 최종 시퀀스로 전달합니다.

var seq1 = Observable.Interval(TimeSpan.FromSeconds(1))
           .Do(x => Console.WriteLine(x.ToString()))
           .Buffer(5)
           .Do(x => Console.WriteLine("buffer is full"))
           .Subscribe(x => Console.WriteLine("Sum of the buffer is " + x.Sum()));
Console.ReadKey();

이 샘플에서 볼 수 있듯이 구독은 일련의 관찰 가능한 연속 시퀀스의 수신자 끝에 있습니다. 처음에는 Interval 연산자를 사용하여 관찰 가능한 정수 시퀀스를 1초 단위로 만듭니다. 그런 다음 버퍼 연산자를 사용하여 5개 항목을 버퍼에 넣고 버퍼가 가득 찬 경우에만 다른 시퀀스로 보냅니다. 마지막으로 구독 연산자에 전달됩니다. 데이터는 관찰자에게 푸시될 때까지 이러한 모든 중간 시퀀스를 전파합니다. 동일한 방식으로 구독은 소스 시퀀스로 역방향으로 전파됩니다. 이러한 전파 중간에 Do 연산자를 삽입하면 .NET의 Console.WriteLine 또는 C의 printf()를 사용하여 디버깅을 수행하는 것처럼 이러한 데이터 흐름을 "감시"할 수 있습니다.

타임스탬프 연산자를 사용하여 관찰 가능한 시퀀스에 의해 항목이 푸시되는 시간을 확인할 수도 있습니다. 이렇게 하면 정확도를 보장하기 위해 시간 기반 작업의 문제를 해결하는 데 도움이 될 수 있습니다. 원본 시퀀스에 의해 푸시된 각 값이 게시된 시간에 추가되도록 Timestamp 연산자를 쿼리에 연결하는 단순 관찰 가능한 시퀀스 만들기 및 구독 항목의 다음 예제를 기억하세요. 이렇게 하면 이 원본 시퀀스를 구독할 때 해당 값과 타임스탬프를 모두 받을 수 있습니다.

Console.WriteLine(“Current Time: “ + DateTime.Now);

var source = Observable.Timer(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(1))
                       .Timestamp();
using (source.Subscribe(x => Console.WriteLine("{0}: {1}", x.Value, x.Timestamp)))
      {
           Console.WriteLine("Press any key to unsubscribe");
           Console.ReadKey();
      }
Console.WriteLine("Press any key to exit");
Console.ReadKey();

다음과 유사하게 출력됩니다.

Current Time: 5/31/2011 5:35:08 PM

Press any key to unsubscribe

0: 5/31/2011 5:35:13 PM -07:00

1: 5/31/2011 5:35:14 PM -07:00

2: 5/31/2011 5:35:15 PM -07:00

Timestamp 연산자를 사용하여 첫 번째 항목이 실제로 시퀀스 후 5초 후에 푸시되고 각 항목이 1초 후에 게시되는 것을 확인했습니다.

또한 디버깅에 도움이 되도록 람다 식 내에서 중단점을 설정할 수도 있습니다. 일반적으로 특정 값을 표시하여 표시하지 않고 전체 쿼리에 대한 중단점만 설정할 수 있습니다. 이 제한을 해결하려면 쿼리 중간에 Select 연산자를 삽입하고 중단점을 설정하고 Select 문에서 자체 줄에 반환 문을 사용하여 원본으로 동일한 값을 프로젝트할 수 있습니다. 그런 다음 문 줄에서 중단점을 설정하고 쿼리를 return 진행하면서 값을 검사할 수 있습니다.

var seq = Observable.Interval(TimeSpan.FromSeconds(1))
          .Do(x => Console.WriteLine(x.ToString()))
          .Buffer(5)
          .Select(y => { 
                  return y; }) // set a breakpoint at this line
          .Do(x => Console.WriteLine("buffer is full"))
          .Subscribe(x => Console.WriteLine("Sum of the buffer is " + x.Sum()));
Console.ReadKey();

이 예제에서는 중단점이 줄에 설정됩니다 return y . 프로그램을 디버그하면 변수가 y로컬 창에 표시되고 총 개수를 (5)검사할 수 있습니다. 를 확장 y하면 해당 값 및 형식을 포함하여 목록의 각 항목을 검사할 수도 있습니다.

또는 람다 식을 문 람다 식으로 변환하고, 문이 자체 줄에 있도록 코드 서식을 지정한 다음 중단점을 설정할 수 있습니다.

디버깅을 완료한 후 Do 및 Select 호출을 제거할 수 있습니다.

참고 항목

개념

단순 관찰 가능한 시퀀스 만들기 및 구독
LINQ 연산자를 사용하여 관찰 가능한 시퀀스 쿼리
스케줄러 사용