![Page 1: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/1.jpg)
1
Tamir Dresher (@tamir_dresher)
Senior Software ArchitectJ
Testing Time and Concurrency with Rx
1
![Page 2: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/2.jpg)
2
• Author of Rx.NET in Action (manning publications)
• Software architect, consultant and instructor
• Software Engineering Lecturer @ Ruppin Academic Center
• OzCode (www.oz-code.com) Evangelist
@[email protected]://www.TamirDresher.com.
About Me
![Page 3: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/3.jpg)
Reactive Extensions (Rx)
Your headache relief pill to Asynchronous Event
based applications
AsyncPush
Triggers
Events
3
![Page 4: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/4.jpg)
Reacting to changes
4
![Page 5: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/5.jpg)
IEnumerable<Message> LoadMessages(string hashtag)
{
var statuses = facebook.Search(hashtag);
var tweets = twitter.Search(hashtag);
var updates = linkedin.Search(hashtag);
return statuses.Concat(tweets).Concat(updates);
}
Twitter App
Pull ModelPull Model
5
![Page 6: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/6.jpg)
???? LoadMessages(string hashtag)
{
facebook.Search(hashtag);
twitter.Search(hashtag);
linkedin.Search(hashtag);
}
DoSomething( )msg
Push ModelPush Model
6
![Page 7: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/7.jpg)
namespace System{
public interface IObservable<out T>{
IDisposable Subscribe(IObserver<T> observer);}
public interface IObserver<in T>{
void OnNext(T value);void OnError(Exception error);void OnCompleted();
}}
InterfacesInterfaces
7
![Page 8: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/8.jpg)
observable observer
Subscribe(observer)
subscription
OnNext(X1)
OnNext(Xn)⁞
⁞
IDisposable
Observables and ObserversObservables and Observers
8
![Page 9: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/9.jpg)
OnCompleted()
OnError(Exception)
observable observer
⁞
Observables and ObserversObservables and Observers
9
![Page 10: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/10.jpg)
Rx packages
10
![Page 11: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/11.jpg)
Push ModelPush Model with Rx Observables
class ReactiveSocialNetworksManager{
//members
public IObservable<Message> ObserveMessages(string hashtag){
:}
}
var mgr = new ReactiveSocialNetworksManager();
mgr.ObserveMessages("Rx").Subscribe(
msg => Console.WriteLine($"Observed:{msg} \t"),ex => { /*OnError*/ },() => { /*OnCompleted*/ });
11
![Page 12: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/12.jpg)
Creating Observables
12
![Page 13: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/13.jpg)
Observable.Range(1, 10)
.Subscribe(x => Console.WriteLine(x));
Observable.Interval(TimeSpan.FromSeconds(1)).Subscribe(x => Console.WriteLine(x));
Observable.FromEventPattern(SearchBox, "TextChanged")
⁞
⁞1 sec 1 sec
Observables Factories
⁞
Observables Factories
13
![Page 14: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/14.jpg)
Observable Queries (Rx Operators)
14
![Page 15: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/15.jpg)
Filtering Projection Partitioning Joins Grouping Set
Element Generation Quantifiers Aggregation Error HandlingTime and Concurrency
Where
OfType
Select
SelectMany
Materialize
Skip
Take
TakeUntil
CombineLatest
Concat
join
GroupBy
GroupByUntil
Buffer
Distinct
DistinctUntilChanged
Timeout
TimeInterval
ElementAt
First
Single
Range
Repeat
Defer
All
Any
Contains
Sum
Average
Scan
Catch
OnErrorResumeNext
Using
Rx operators
15
![Page 16: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/16.jpg)
Reactive SearchReactive Search
16
![Page 17: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/17.jpg)
1. At least 3 characters2. Don’t overflow server (0.5 sec delay)3. Don’t send the same string again4. Discard results if another search was requested
Reactive Search - RulesReactive Search - Rules
17
![Page 18: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/18.jpg)
18
![Page 19: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/19.jpg)
19
![Page 20: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/20.jpg)
20
Where(s => s.Length > 2 )
![Page 21: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/21.jpg)
21
Where(s => s.Length > 2 )
Throttle(TimeSpan.FromSeconds(0.5))
![Page 22: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/22.jpg)
22
Where(s => s.Length > 2 )
Throttle(TimeSpan.FromSeconds(0.5))
DistinctUntilChanged()
![Page 23: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/23.jpg)
24
Select(text => SearchAsync(text))
“REA”
“REAC”
Switch()
“REA” results are ignored since we switched to the “REAC” results
![Page 24: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/24.jpg)
Abstracting Time and Concurrency
25
![Page 25: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/25.jpg)
Thread Pool
Task Scheduler
other
SchedulersSchedulers
26
![Page 26: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/26.jpg)
public interface IScheduler{
DateTimeOffset Now { get; }
IDisposable Schedule<TState>( TState state, Func<IScheduler, TState, IDisposable> action);
IDisposable Schedule<TState>(TimeSpan dueTime, TState state,Func<IScheduler, TState, IDisposable> action);
IDisposable Schedule<TState>(DateTimeOffset dueTime, TState state,Func<IScheduler, TState, IDisposable> action);
}
27
![Page 27: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/27.jpg)
//// Runs a timer on the default scheduler//IObservable TimeSpan
//// Every operator that introduces concurrency// has an overload with an IScheduler//IObservable T TimeSpan
IScheduler scheduler);
Parameterizing ConcurrencyParameterizing Concurrency
28
![Page 28: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/28.jpg)
textChanged.Throttle(TimeSpan.FromSeconds(0.5),
DefaultScheduler.Instance).DistinctUntilChanged().SelectMany(text => SearchAsync(text)).Switch().Subscribe(/*handle the results*/);
29
![Page 29: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/29.jpg)
//// runs the observer callbacks on the specified // scheduler.//IObservable T ObserveOn<T>(IScheduler);
//// runs the observer subscription and unsubsciption on// the specified scheduler.//IObservable T SubscribeOn<T>(IScheduler)
Changing Execution ContextChanging Execution Context
30
![Page 30: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/30.jpg)
textChanged.Throttle(TimeSpan.FromSeconds(0.5)).DistinctUntilChanged().Select(text => SearchAsync(text)).Switch().ObserveOn(DispatcherScheduler.Current).Subscribe(/*handle the results*/);
Changing Execution ContextChanging Execution Context
31
![Page 31: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/31.jpg)
textChanged.Throttle(TimeSpan.FromSeconds(0.5)).DistinctUntilChanged().Select(text => SearchAsync(text)).Switch().ObserveOnDispatcher().Subscribe(/*handle the results*/);
Changing Execution ContextChanging Execution Context
32
![Page 32: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/32.jpg)
Virtual Time
What is time?
Time can be a anything that is sequential and comparable
33
“Time is the indefinite continued progress of existence and events ... Time
is a component quantity of various measurements used
to sequence events, to compare the duration of events or the intervals
between them…”
https://en.wikipedia.org/wiki/Time
![Page 33: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/33.jpg)
Virtual Time Scheduler
34
public abstract class VirtualTimeSchedulerBase<TAbsolute, TRelative> : IScheduler,
IServiceProvider, IStopwatchProvider
where TAbsolute : IComparable<TAbsolute>
{
public TAbsolute Clock { get; protected set;}
public void Start()
public void Stop()
public void AdvanceTo(TAbsolute time)
public void AdvanceBy(TRelative time)
...
}
public class TestScheduler : VirtualTimeScheduler<long, long>{
...}
![Page 34: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/34.jpg)
Testing Rx
35
![Page 35: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/35.jpg)
1. At least 3 characters One letter word, No Search performed
2. Don’t overflow server (0.5 sec delay) 2 words typed, 0.2 sec gap, search only the last
3. Don’t send the same string again 2 words, 1 sec gap, same value, search only first
4. Discard results if another search was requested 2 words, 1 sec gap, slow first search, fast last search, last results shown
Reactive Search - RulesReactive Search – Rules Tests
36
![Page 36: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/36.jpg)
Questions and Answers
Q: How can we test the code without a real user interaction?
Q: How can we test the code without a real server?
Q: How can we test the code deterministically without a real asynchronicity and concurrency?
Q: How can we test the code without REALLY waiting for the time to pass?
37
A: Separation of concerns. Separate the logic from the view
A: Enable Dependency Injection and mock the service client
A: Leverage the Rx Schedulers and provide a Scheduler you can control via DI
A: Leverage the Rx TestScheduler which provides a virtualization of time
![Page 37: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/37.jpg)
Separating the logic from the view
38
SearchView(Presentation, Logic, State)
SearchView(Presentation)
SearchViewModel(Logic, State)
Before
After
![Page 38: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/38.jpg)
Separating the logic from the view
39
<Window x:Class="TestableReactiveSearch.SearchView"><DockPanel>
<TextBox x:Name="SearchBox"Text="{Binding SearchTerm …}"DockPanel.Dock="Top“/>
<ListBox x:Name="SearchResults"ItemsSource="{Binding SearchResults}“/>
</DockPanel></Window>
public class SearchViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public SearchViewModel()
{
// Rx query
}
public string SearchTerm { get { ... } set { ... } }
public IEnumerable<string> SearchResults { get { ... } set { ... } }}
SearchView.xaml
SearchViewModel.cs
![Page 39: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/39.jpg)
Separating the logic from the view – fixing the Rx query
40
public SearchViewModel()
{
var terms =
Observable.FromEventPattern<PropertyChangedEventArgs>(this, nameof(PropertyChanged))
.Where(e => e.EventArgs.PropertyName == nameof(SearchTerm))
.Select(_ => SearchTerm);
_subscription =
terms
.Where(txt => txt.Length >= 3)
.Throttle(TimeSpan.FromSeconds(0.5))
.DistinctUntilChanged()
.Select(txt => searchServiceClient.SearchAsync(txt))
.Switch()
.ObserveOnDispatcher()
.Subscribe(
results => SearchResults = results,
err => { Debug.WriteLine(err); },
() => { /* OnCompleted */ });
}
Same query as before
![Page 40: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/40.jpg)
Injecting the Search Service client
41
public class SearchViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public SearchViewModel()
{
// rest of rx query
}
...}
![Page 41: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/41.jpg)
Injecting the Search Service client
42
public class SearchViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public SearchViewModel(ISearchServiceClient searchServiceClient)
{
// rest of rx query
}
...}
![Page 42: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/42.jpg)
Simple test – first try
43
[TestMethod]
public void Search_OneLetterWord_NoSearchSentToService()
{
var fakeServiceClient = Substitute.For<ISearchServiceClient>();
var vm = new SearchViewModel(fakeServiceClient);
vm.SearchTerm = "A";
fakeServiceClient.DidNotReceive().SearchAsync("A");
}
![Page 43: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/43.jpg)
Injecting concurrency
44
public interface IConcurrencyProvider{
IScheduler TimeBasedOperations { get; }IScheduler Task { get; }IScheduler Thread { get; }IScheduler Dispatcher { get; }
}class ConcurrencyProvider : IConcurrencyProvider{
public ConcurrencyProvider(){
TimeBasedOperations = DefaultScheduler.Instance;Task = TaskPoolScheduler.Default;Thread = NewThreadScheduler.Default;Dispatcher=DispatcherScheduler.Current;
}
public IScheduler TimeBasedOperations { get; }public IScheduler Task { get; }public IScheduler Thread { get; }public IScheduler Dispatcher { get; }
}
![Page 44: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/44.jpg)
Injecting concurrency
45
public SearchViewModel(ISearchServiceClient searchServiceClient,
IConcurrencyProvider concurrencyProvider)
{
var terms =
Observable.FromEventPattern<PropertyChangedEventArgs>(this, nameof(PropertyChanged))
.Where(e => e.EventArgs.PropertyName == nameof(SearchTerm)).Select(_=>SearchTerm);
_subscription =
terms
.Where(txt => txt.Length >= 3)
.Throttle(TimeSpan.FromSeconds(0.5), concurrencyProvider.Thread)
.DistinctUntilChanged()
.Select(txt => searchServiceClient.SearchAsync(txt))
.Switch()
.ObserveOn(concurrencyProvider.Dispatcher)
.Subscribe(
results => SearchResults = results,
err => { Debug.WriteLine(err); },
() => { /* OnCompleted */ });
}
![Page 45: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/45.jpg)
Simplest Rx Test
Install-Package Microsoft.Reactive.Testing
To simplify the Rx testing, derive your test class from ReactiveTest
46
using Microsoft.Reactive.Testing;
[TestClass]
public class SearchViewModelTests : ReactiveTest
{
// Test Methods
}
![Page 46: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/46.jpg)
Test 1: Search is sent after half a sec
47
const long ONE_SECOND = TimeSpan.TicksPerSecond;
[TestMethod]
public void MoreThanThreeLetters_HalfSecondGap_SearchSentToService()
{
var fakeServiceClient = Substitute.For<ISearchServiceClient>();
var fakeConcurrencyProvider = Substitute.For<IConcurrencyProvider>();
var testScheduler = new TestScheduler();
fakeConcurrencyProvider.ReturnsForAll<IScheduler>(testScheduler);
var vm = new SearchViewModel(fakeServiceClient, fakeConcurrencyProvider);
testScheduler.Start();
vm.SearchTerm = "reactive";
testScheduler.AdvanceBy(ONE_SECOND / 2);
fakeServiceClient.Received().SearchAsync("reactive");
}
![Page 47: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/47.jpg)
TestScheduler
TestScheduler provides two methods for creating observables:CreateColdObservable – Creates an observable that emits its value relatively to when each observer subscribes.
CreateHotObservable – Creates and observable that emits its values regardless to the observer subscription time, and each emission is configured to the absolute scheduler clock
48
var testScheduler = new TestScheduler();
ITestableObservable<int> coldObservable = testScheduler.CreateColdObservable<int>(OnNext<int>(20, 1),OnNext<int>(40, 2),OnCompleted<int>(60)
);
![Page 48: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/48.jpg)
Test 2: first search is discarded if another search happens
49
public void TwoValidWords_SlowSearchThenFastSearch_SecondSearchResultsOnly()
{
var fakeServiceClient = Substitute.For<ISearchServiceClient>();
var fakeConcurrencyProvider = Substitute.For<IConcurrencyProvider>();
var testScheduler = new TestScheduler();
fakeConcurrencyProvider.ReturnsForAll<IScheduler>(testScheduler);
fakeServiceClient.SearchAsync("first").Returns(testScheduler.CreateColdObservable(
OnNext<IEnumerable<string>>(2 * ONE_SECOND, new[] {"first"}), ...);
fakeServiceClient.SearchAsync("second").Returns(testScheduler.CreateColdObservable(
OnNext<IEnumerable<string>>(1, new[] { "second" }), ...);
var vm = new SearchViewModel(fakeServiceClient, fakeConcurrencyProvider);
testScheduler.Start();
vm.SearchTerm = "first";
testScheduler.AdvanceBy(ONE_SECOND);
vm.SearchTerm = "second";
testScheduler.AdvanceBy(5 * ONE_SECOND);
Assert.AreEqual("second", vm.SearchResults.First());
}
![Page 49: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/49.jpg)
Summary
Pull vs. Push model
Rx operators
Building Rx queries
Rx Concurrency Model
Virtual Time
Testing Time and Concurrency with TestScheduler
50
![Page 50: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/50.jpg)
Your headache relief pill to Asynchronous and
Event based applications
AsyncPush
Triggers
Events
Reactive ExtensionsReactive Extensions
51
![Page 51: Testing Time and Concurrency with Rx - TestCon Moscowtestconf.ru/.../2017/...Testing-Time-and-Concurrency-Rx.NET-Schedulers.pdf · 1 Tamir Dresher (@tamir_dresher) Senior Software](https://reader030.vdocuments.us/reader030/viewer/2022041221/5e0a5b7a1ea94018dd453f3e/html5/thumbnails/51.jpg)
www.reactivex.io github.com/Reactive-Extensionswww.manning.com/dresher
Thank You
Tamir Dresher (@tamir_dresher)
52