SlideShare a Scribd company logo
1 of 26
Marble Testing
By Bruno Vollino
bruno.vollino@ilegra.com
Agenda
What will be presented?
● Marble diagrams
● Marble testing RxJS Observables
● Angular examples
Why?
Asynchronous code is hard to
understand and to test.
Reactive processing using
Observables and Subscribers is no
exception.
Marble diagrams and marble tests
may help in this particular case.
What a marble
diagram is?
Particularly useful for representing
reactive streams (observables) and
their operators.
Can depict the output of multiple
asynchronous observables in a given
time frame.
Marble diagram example http://reactivex.io/documentation/observable.html
Marble diagram example http://reactivex.io/documentation/observable.html
We can call this “a marble”
Marble diagram example http://reactivex.io/documentation/observable.html
Marble diagram example http://reactivex.io/documentation/observable.html
Marble diagram example http://reactivex.io/documentation/observable.html
ReactiveX Filter operator http://reactivex.io/documentation/operators/filter.html
ReactiveX Filter operator http://reactivex.io/documentation/operators/filter.html
Time slots where nothing was emmited
ReactiveX Zip operator http://reactivex.io/documentation/operators/zip.html
ReactiveX Zip operator http://reactivex.io/documentation/operators/zip.html
Representation of time is
important to show how an
operator works
What is a marble
test?
● Test that use mocked
observables expressed by
marbles.
● Time is simulated: observables
will progress synchronously, in
fixed time frames, according
with their marble descriptions.
Marble testing the Zip operator
Given two source observables represented by these marbles
When the Zip operator is applied to them
It should result in an observable represented by this marble
Marble testing the Zip operator
Given two source observables represented by these marbles
When the Zip operator is applied to them
It should result in an observable represented by this marble
T1 T2
Simulated
Time slots
Marble testing the Zip operator
it('should zip elements', () => {
// Given two source and a result observable represented by these marbles
const numbers$: Observable<number> = cold(' -1-2-----3-4--5-|');
const chars$: Observable<string> = cold(' --A-B--CD-------|');
const expected$: Observable<[string, string]> = cold('--X-Y----W-Z----|', {
X: ['1', 'A'],
Y: ['2', 'B'],
W: ['3', 'C'],
Z: ['4', 'D'] // emitted objects have to be mapped to the symbols in the marble
});
// When the zip operator is applied to the source marbles
const actual$ = zip(numbers$, chars$);
getTestScheduler().flush();
// The actual result observable should be equal to the expected marble
expect(actual$).toBeObservable(expected$);
});
Angular examples
Random movie spoiler app
Angular examples
● Test the RandomSpoilerComponent,
that asynchronously loads a random
spoiler for the view.
● Test the SpoilerService, that gets a
list of spoilers from a REST service
and returns a random spoiler.
Marble testing an Angular component
export class RandomSpoilerComponent {
spoiler: Spoiler; // property displayed by the view
constructor(private spoilerService: SpoilerService, private snackBar: MatSnackBar) { }
/* Called when user clicks the button. Gets a random spoiler observable from the spoilerService.
If it receives a new spoiler, assign it to the 'spoiler' property.
If it receives an error, display an error message. */
nextSpoiler() {
this.spoilerService.getRandomSpoiler().pipe(take(1)).subscribe({
next: (newSpoiler) => {
this.spoiler = newSpoiler;
},
error: () => {
this.snackBar.open('It was not possible to load your next spoiler.', null, {duration: 3000});
}});
}
}
Marble testing an Angular component
it('should load a new spoiler', () => {
// given the spoilerService returns a single spoiler, represented by a marble
const spoiler = { id: 1, spoiler: 'Bruce Willis is dead' };
const serviceSpoiler$ = cold('----a', {a: spoiler}); // delayed to ensure async response is handled
spyOn(spoilerService, 'getRandomSpoiler').and.returnValue(serviceSpoiler$);
// when the component loads the next random spoiler
component.nextSpoiler();
getTestScheduler().flush();
// then the new spoiler should become available for the view in a property
expect(component.spoiler).toBe(spoiler);
});
Marble testing an Angular component
it('should display a message message when an error occurs while getting a spoiler', () => {
// given the spoilerService returns an error
const serviceSpoiler$ = cold('----#'); // in ascii marbles errors are represented by a '#'
spyOn(spoilerService, 'getRandomSpoiler').and.returnValue(serviceSpoiler$);
spyOn(snackBar, 'open');
// when the component tries to load the next random spoiler
component.nextSpoiler();
getTestScheduler().flush();
// then it should display an error message in a 'snack bar' notification
expect(snackBar.open).toHaveBeenCalledWith(
'It was not possible to load your next spoiler.', null, {duration: 3000});
});
Marble testing an Angular service
export class SpoilerService {
constructor(private httpClient: HttpClient) { }
// Requests a list of spoilers from a REST service map it to return a single random spoiler
public getRandomSpoiler(): Observable<Spoiler> {
const uri = 'http://localhost:3000/spoilers/';
return this.httpClient.get<Spoiler[]>(uri) // [#1, #2, #3]
.pipe(
map(spoilers => spoilers[this.randomIndex(spoilers)]) // #X
);
}
randomIndex(spoilers) {
return Math.floor(Math.random() * spoilers.length);
}
}
Marble testing an Angular service
it('should get a random spoiler', () => {
// given the HTTP request will return a list of spoilers
const spoiler1: Spoiler = { id: 1, spoiler: 'Bruce Willis is dead'};
const spoiler2: Spoiler = { id: 2, spoiler: "Brad Pitt is Edward Norton's alter ego"};
const spoilers$ = cold('---L', {L: [spoiler1, spoiler2]});
const expectedRandomSpoiler$ = cold('---a', {a: spoiler2} );
spyOn(httpClient, 'get').and.returnValue(spoilers$);
spyOn(service, 'randomIndex').and.returnValue(1);
// when we get a random spoiler
const actualRandomSpoiler$ = service.getRandomSpoiler();
getTestScheduler().flush();
// then the service must return a single random spoiler in an observable
expect(actualRandomSpoiler$).toBeObservable(expectedRandomSpoiler$);
});
References
● https://dev.to/mokkapps/how-i-
write-marble-tests-for-rxjs-
observables-in-angular-4l0k
● http://reactivex.io/documentation
/operators/
● https://github.com/ReactiveX/rxj
s/blob/master/docs_app/content/
guide/testing/marble-testing.md
● https://medium.com/@benlesh/h
ot-vs-cold-observables-
f8094ed53339
Questions?
Marble vs Marbles

More Related Content

What's hot

Unit testing in iOS featuring OCUnit, GHUnit & OCMock
Unit testing in iOS featuring OCUnit, GHUnit & OCMockUnit testing in iOS featuring OCUnit, GHUnit & OCMock
Unit testing in iOS featuring OCUnit, GHUnit & OCMock
Robot Media
 

What's hot (20)

Workshop 5: JavaScript testing
Workshop 5: JavaScript testingWorkshop 5: JavaScript testing
Workshop 5: JavaScript testing
 
Side effects-con-redux
Side effects-con-reduxSide effects-con-redux
Side effects-con-redux
 
Clean code via dependency injection + guice
Clean code via dependency injection + guiceClean code via dependency injection + guice
Clean code via dependency injection + guice
 
Code generation with javac plugin
Code generation with javac pluginCode generation with javac plugin
Code generation with javac plugin
 
Introductionandgreetings
IntroductionandgreetingsIntroductionandgreetings
Introductionandgreetings
 
Workshop 1: Good practices in JavaScript
Workshop 1: Good practices in JavaScriptWorkshop 1: Good practices in JavaScript
Workshop 1: Good practices in JavaScript
 
Coroutines in Kotlin
Coroutines in KotlinCoroutines in Kotlin
Coroutines in Kotlin
 
Coroutines in Kotlin. UA Mobile 2017.
Coroutines in Kotlin. UA Mobile 2017.Coroutines in Kotlin. UA Mobile 2017.
Coroutines in Kotlin. UA Mobile 2017.
 
A Spin-off: CryEngine 3 SDK Checked with CppCat
A Spin-off: CryEngine 3 SDK Checked with CppCatA Spin-off: CryEngine 3 SDK Checked with CppCat
A Spin-off: CryEngine 3 SDK Checked with CppCat
 
Reactive, component 그리고 angular2
Reactive, component 그리고  angular2Reactive, component 그리고  angular2
Reactive, component 그리고 angular2
 
Serious Sam shooter anniversary - finding bugs in the code of the Serious Eng...
Serious Sam shooter anniversary - finding bugs in the code of the Serious Eng...Serious Sam shooter anniversary - finding bugs in the code of the Serious Eng...
Serious Sam shooter anniversary - finding bugs in the code of the Serious Eng...
 
Top 10 C# projects errors found in 2016
Top 10 C# projects errors found in 2016Top 10 C# projects errors found in 2016
Top 10 C# projects errors found in 2016
 
Spring RTS Engine Checkup
Spring RTS Engine CheckupSpring RTS Engine Checkup
Spring RTS Engine Checkup
 
Analyzing Wine: One Year Later
Analyzing Wine: One Year LaterAnalyzing Wine: One Year Later
Analyzing Wine: One Year Later
 
Boost statechart library
Boost statechart libraryBoost statechart library
Boost statechart library
 
Understanding Asynchronous JavaScript
Understanding Asynchronous JavaScriptUnderstanding Asynchronous JavaScript
Understanding Asynchronous JavaScript
 
CppCat Checks OpenMW: Not All is Fine in the Morrowind Universe
CppCat Checks OpenMW: Not All is Fine in the Morrowind UniverseCppCat Checks OpenMW: Not All is Fine in the Morrowind Universe
CppCat Checks OpenMW: Not All is Fine in the Morrowind Universe
 
Introduction to Swift
Introduction to SwiftIntroduction to Swift
Introduction to Swift
 
PVS-Studio and Continuous Integration: TeamCity. Analysis of the Open RollerC...
PVS-Studio and Continuous Integration: TeamCity. Analysis of the Open RollerC...PVS-Studio and Continuous Integration: TeamCity. Analysis of the Open RollerC...
PVS-Studio and Continuous Integration: TeamCity. Analysis of the Open RollerC...
 
Unit testing in iOS featuring OCUnit, GHUnit & OCMock
Unit testing in iOS featuring OCUnit, GHUnit & OCMockUnit testing in iOS featuring OCUnit, GHUnit & OCMock
Unit testing in iOS featuring OCUnit, GHUnit & OCMock
 

Similar to Marble testing

JavaScript Growing Up
JavaScript Growing UpJavaScript Growing Up
JavaScript Growing Up
David Padbury
 
Angular 2.0: Brighter future?
Angular 2.0: Brighter future?Angular 2.0: Brighter future?
Angular 2.0: Brighter future?
Eugene Zharkov
 

Similar to Marble testing (20)

angular fundamentals.pdf
angular fundamentals.pdfangular fundamentals.pdf
angular fundamentals.pdf
 
Testing React hooks with the new act function
Testing React hooks with the new act functionTesting React hooks with the new act function
Testing React hooks with the new act function
 
JavaScript
JavaScriptJavaScript
JavaScript
 
Angular 2 Unit Testing.pptx
Angular 2 Unit Testing.pptxAngular 2 Unit Testing.pptx
Angular 2 Unit Testing.pptx
 
Explaination of angular
Explaination of angularExplaination of angular
Explaination of angular
 
Containers & Dependency in Ember.js
Containers & Dependency in Ember.jsContainers & Dependency in Ember.js
Containers & Dependency in Ember.js
 
Rapid prototyping and easy testing with ember cli mirage
Rapid prototyping and easy testing with ember cli mirageRapid prototyping and easy testing with ember cli mirage
Rapid prototyping and easy testing with ember cli mirage
 
Testing with Kotlin
Testing with KotlinTesting with Kotlin
Testing with Kotlin
 
Ultimate Introduction To AngularJS
Ultimate Introduction To AngularJSUltimate Introduction To AngularJS
Ultimate Introduction To AngularJS
 
Workshop 23: ReactJS, React & Redux testing
Workshop 23: ReactJS, React & Redux testingWorkshop 23: ReactJS, React & Redux testing
Workshop 23: ReactJS, React & Redux testing
 
Expert JavaScript tricks of the masters
Expert JavaScript  tricks of the mastersExpert JavaScript  tricks of the masters
Expert JavaScript tricks of the masters
 
JavaScript Growing Up
JavaScript Growing UpJavaScript Growing Up
JavaScript Growing Up
 
Curso de Node.js e MongoDB - 16
Curso de Node.js e MongoDB - 16Curso de Node.js e MongoDB - 16
Curso de Node.js e MongoDB - 16
 
Integrating Angular js & three.js
Integrating Angular js & three.jsIntegrating Angular js & three.js
Integrating Angular js & three.js
 
Testing frontends with nightwatch & saucelabs
Testing frontends with nightwatch & saucelabsTesting frontends with nightwatch & saucelabs
Testing frontends with nightwatch & saucelabs
 
Android RenderScript on LLVM
Android RenderScript on LLVMAndroid RenderScript on LLVM
Android RenderScript on LLVM
 
Marble Testing RxJS streams
Marble Testing RxJS streamsMarble Testing RxJS streams
Marble Testing RxJS streams
 
Angular 2.0: Brighter future?
Angular 2.0: Brighter future?Angular 2.0: Brighter future?
Angular 2.0: Brighter future?
 
Func up your code
Func up your codeFunc up your code
Func up your code
 
JavaScript for real men
JavaScript for real menJavaScript for real men
JavaScript for real men
 

Recently uploaded

Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Medical / Health Care (+971588192166) Mifepristone and Misoprostol tablets 200mg
 
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
VictoriaMetrics
 
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
masabamasaba
 
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
chiefasafspells
 

Recently uploaded (20)

MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
MarTech Trend 2024 Book : Marketing Technology Trends (2024 Edition) How Data...
 
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
WSO2CON 2024 - Cloud Native Middleware: Domain-Driven Design, Cell-Based Arch...
 
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
Abortion Pill Prices Tembisa [(+27832195400*)] 🏥 Women's Abortion Clinic in T...
 
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
OpenChain - The Ramifications of ISO/IEC 5230 and ISO/IEC 18974 for Legal Pro...
 
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa%in tembisa+277-882-255-28 abortion pills for sale in tembisa
%in tembisa+277-882-255-28 abortion pills for sale in tembisa
 
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
%in Bahrain+277-882-255-28 abortion pills for sale in Bahrain
 
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park %in ivory park+277-882-255-28 abortion pills for sale in ivory park
%in ivory park+277-882-255-28 abortion pills for sale in ivory park
 
VTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learnVTU technical seminar 8Th Sem on Scikit-learn
VTU technical seminar 8Th Sem on Scikit-learn
 
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
Crypto Cloud Review - How To Earn Up To $500 Per DAY Of Bitcoin 100% On AutoP...
 
Artyushina_Guest lecture_YorkU CS May 2024.pptx
Artyushina_Guest lecture_YorkU CS May 2024.pptxArtyushina_Guest lecture_YorkU CS May 2024.pptx
Artyushina_Guest lecture_YorkU CS May 2024.pptx
 
%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand%in Midrand+277-882-255-28 abortion pills for sale in midrand
%in Midrand+277-882-255-28 abortion pills for sale in midrand
 
WSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go PlatformlessWSO2CON2024 - It's time to go Platformless
WSO2CON2024 - It's time to go Platformless
 
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
Large-scale Logging Made Easy: Meetup at Deutsche Bank 2024
 
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
%+27788225528 love spells in new york Psychic Readings, Attraction spells,Bri...
 
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
Love witchcraft +27768521739 Binding love spell in Sandy Springs, GA |psychic...
 
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
%in Rustenburg+277-882-255-28 abortion pills for sale in Rustenburg
 
WSO2CON 2024 Slides - Unlocking Value with AI
WSO2CON 2024 Slides - Unlocking Value with AIWSO2CON 2024 Slides - Unlocking Value with AI
WSO2CON 2024 Slides - Unlocking Value with AI
 
WSO2CON 2024 - How to Run a Security Program
WSO2CON 2024 - How to Run a Security ProgramWSO2CON 2024 - How to Run a Security Program
WSO2CON 2024 - How to Run a Security Program
 
WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...
WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...
WSO2CON 2024 - Building the API First Enterprise – Running an API Program, fr...
 
WSO2Con2024 - From Blueprint to Brilliance: WSO2's Guide to API-First Enginee...
WSO2Con2024 - From Blueprint to Brilliance: WSO2's Guide to API-First Enginee...WSO2Con2024 - From Blueprint to Brilliance: WSO2's Guide to API-First Enginee...
WSO2Con2024 - From Blueprint to Brilliance: WSO2's Guide to API-First Enginee...
 

Marble testing

  • 1. Marble Testing By Bruno Vollino bruno.vollino@ilegra.com
  • 2. Agenda What will be presented? ● Marble diagrams ● Marble testing RxJS Observables ● Angular examples
  • 3. Why? Asynchronous code is hard to understand and to test. Reactive processing using Observables and Subscribers is no exception. Marble diagrams and marble tests may help in this particular case.
  • 4. What a marble diagram is? Particularly useful for representing reactive streams (observables) and their operators. Can depict the output of multiple asynchronous observables in a given time frame.
  • 5. Marble diagram example http://reactivex.io/documentation/observable.html
  • 6. Marble diagram example http://reactivex.io/documentation/observable.html We can call this “a marble”
  • 7. Marble diagram example http://reactivex.io/documentation/observable.html
  • 8. Marble diagram example http://reactivex.io/documentation/observable.html
  • 9. Marble diagram example http://reactivex.io/documentation/observable.html
  • 10. ReactiveX Filter operator http://reactivex.io/documentation/operators/filter.html
  • 11. ReactiveX Filter operator http://reactivex.io/documentation/operators/filter.html Time slots where nothing was emmited
  • 12. ReactiveX Zip operator http://reactivex.io/documentation/operators/zip.html
  • 13. ReactiveX Zip operator http://reactivex.io/documentation/operators/zip.html Representation of time is important to show how an operator works
  • 14. What is a marble test? ● Test that use mocked observables expressed by marbles. ● Time is simulated: observables will progress synchronously, in fixed time frames, according with their marble descriptions.
  • 15. Marble testing the Zip operator Given two source observables represented by these marbles When the Zip operator is applied to them It should result in an observable represented by this marble
  • 16. Marble testing the Zip operator Given two source observables represented by these marbles When the Zip operator is applied to them It should result in an observable represented by this marble T1 T2 Simulated Time slots
  • 17. Marble testing the Zip operator it('should zip elements', () => { // Given two source and a result observable represented by these marbles const numbers$: Observable<number> = cold(' -1-2-----3-4--5-|'); const chars$: Observable<string> = cold(' --A-B--CD-------|'); const expected$: Observable<[string, string]> = cold('--X-Y----W-Z----|', { X: ['1', 'A'], Y: ['2', 'B'], W: ['3', 'C'], Z: ['4', 'D'] // emitted objects have to be mapped to the symbols in the marble }); // When the zip operator is applied to the source marbles const actual$ = zip(numbers$, chars$); getTestScheduler().flush(); // The actual result observable should be equal to the expected marble expect(actual$).toBeObservable(expected$); });
  • 19. Angular examples ● Test the RandomSpoilerComponent, that asynchronously loads a random spoiler for the view. ● Test the SpoilerService, that gets a list of spoilers from a REST service and returns a random spoiler.
  • 20. Marble testing an Angular component export class RandomSpoilerComponent { spoiler: Spoiler; // property displayed by the view constructor(private spoilerService: SpoilerService, private snackBar: MatSnackBar) { } /* Called when user clicks the button. Gets a random spoiler observable from the spoilerService. If it receives a new spoiler, assign it to the 'spoiler' property. If it receives an error, display an error message. */ nextSpoiler() { this.spoilerService.getRandomSpoiler().pipe(take(1)).subscribe({ next: (newSpoiler) => { this.spoiler = newSpoiler; }, error: () => { this.snackBar.open('It was not possible to load your next spoiler.', null, {duration: 3000}); }}); } }
  • 21. Marble testing an Angular component it('should load a new spoiler', () => { // given the spoilerService returns a single spoiler, represented by a marble const spoiler = { id: 1, spoiler: 'Bruce Willis is dead' }; const serviceSpoiler$ = cold('----a', {a: spoiler}); // delayed to ensure async response is handled spyOn(spoilerService, 'getRandomSpoiler').and.returnValue(serviceSpoiler$); // when the component loads the next random spoiler component.nextSpoiler(); getTestScheduler().flush(); // then the new spoiler should become available for the view in a property expect(component.spoiler).toBe(spoiler); });
  • 22. Marble testing an Angular component it('should display a message message when an error occurs while getting a spoiler', () => { // given the spoilerService returns an error const serviceSpoiler$ = cold('----#'); // in ascii marbles errors are represented by a '#' spyOn(spoilerService, 'getRandomSpoiler').and.returnValue(serviceSpoiler$); spyOn(snackBar, 'open'); // when the component tries to load the next random spoiler component.nextSpoiler(); getTestScheduler().flush(); // then it should display an error message in a 'snack bar' notification expect(snackBar.open).toHaveBeenCalledWith( 'It was not possible to load your next spoiler.', null, {duration: 3000}); });
  • 23. Marble testing an Angular service export class SpoilerService { constructor(private httpClient: HttpClient) { } // Requests a list of spoilers from a REST service map it to return a single random spoiler public getRandomSpoiler(): Observable<Spoiler> { const uri = 'http://localhost:3000/spoilers/'; return this.httpClient.get<Spoiler[]>(uri) // [#1, #2, #3] .pipe( map(spoilers => spoilers[this.randomIndex(spoilers)]) // #X ); } randomIndex(spoilers) { return Math.floor(Math.random() * spoilers.length); } }
  • 24. Marble testing an Angular service it('should get a random spoiler', () => { // given the HTTP request will return a list of spoilers const spoiler1: Spoiler = { id: 1, spoiler: 'Bruce Willis is dead'}; const spoiler2: Spoiler = { id: 2, spoiler: "Brad Pitt is Edward Norton's alter ego"}; const spoilers$ = cold('---L', {L: [spoiler1, spoiler2]}); const expectedRandomSpoiler$ = cold('---a', {a: spoiler2} ); spyOn(httpClient, 'get').and.returnValue(spoilers$); spyOn(service, 'randomIndex').and.returnValue(1); // when we get a random spoiler const actualRandomSpoiler$ = service.getRandomSpoiler(); getTestScheduler().flush(); // then the service must return a single random spoiler in an observable expect(actualRandomSpoiler$).toBeObservable(expectedRandomSpoiler$); });
  • 25. References ● https://dev.to/mokkapps/how-i- write-marble-tests-for-rxjs- observables-in-angular-4l0k ● http://reactivex.io/documentation /operators/ ● https://github.com/ReactiveX/rxj s/blob/master/docs_app/content/ guide/testing/marble-testing.md ● https://medium.com/@benlesh/h ot-vs-cold-observables- f8094ed53339