I brought the book "rxjs in action" and just finish the testing section.
Testing rxjs codes are different then usual testing, because everything are lazy loading.
In the book, they mention two test method, either passing done(I am using QUnit and done signals async code is finish) or marble diagrams.
My question is, which method should I choose, that I have mentioned above?
I have been getting this question a lot from my colleagues. I finally got around to document my ways of testing RxJs on my blog. Since your question seems to be related to RxJs5 i will only quote the relevant part of my post here.
Testing in RxJs5 the RxJs4 way
When you migrate your codebase from RxJs4 towards 5 you will find out that a lot of things have been moved, renamed and above all that the implementation of the TestScheduler is no longer available. RxJs contributor kwonoj has created a compatibility shim to help migration towards RxJs5. You can install it using npm npm install #kwonoj/rxjs-testscheduler-compat. Not all features of the TestScheduler are implemented but the most important .startScheduler is working.
const TestScheduler = require('#kwonoj/rxjs-testscheduler-compat').TestScheduler;
const next = require('#kwonoj/rxjs-testscheduler-compat').next;
const complete = require('#kwonoj/rxjs-testscheduler-compat').complete;
it('works in RxJs5 with the compat package', () => {
const scheduler = new TestScheduler(); // Note; no longer the Rx.TestScheduler
const results = scheduler.startScheduler(
() => Rx.Observable.interval(100, scheduler).take(3),
{ created: 100, subscribed: 200, unsubscribed: 1000 } // NOTE: disposed is now renamed to unsubscribed
);
collectionAssert.assertEqual(res.messages, [
next(200 + 100, 0),
next(200 + 200, 1),
next(200 + 300, 2),
complete(200 + 300)
]);
});
Testing in RxJs5 using the new Marble testing syntax
The RxJs team has introduced marble testing syntax to more visually define how your operator or custom code should operate.
var e1 = hot('----a--^--b-------c--|');
var e2 = hot( '---d-^--e---------f-----|');
var expected = '---(be)----c-f-----|';
expectObservable(e1.merge(e2)).toBe(expected);
At the time of writing this post they have not yet made this approach really easy to use outside of the RxJs5 library itself. There are implementations available to see how to do it yourself. You can also look around in the codebase of RxJs5 to see how to setup your testing framework to do your own marble tests. There is an open issue about documenting testing with RxJs5. I have not yet succeeded to get my testing framework setup to do marble testing in this way.
Time has passed, and now it is definitely possible (even easy) to use these marble tests yourself using TestScheduler. They are a great way to comprehensively test emissions over time, errors, completions and subscriptions in an easy-to-understand format. Here is a sample from their docs:
import { TestScheduler } from 'rxjs/testing';
const testScheduler = new TestScheduler((actual, expected) => {
// asserting the two objects are equal
// e.g. using chai.
expect(actual).deep.equal(expected);
});
// This test will actually run *synchronously*
it('generate the stream correctly', () => {
testScheduler.run(helpers => {
const { cold, expectObservable, expectSubscriptions } = helpers;
const e1 = cold('-a--b--c---|');
const subs = '^----------!';
const expected = '-a-----c---|';
expectObservable(e1.pipe(throttleTime(3, testScheduler))).toBe(expected);
expectSubscriptions(e1.subscriptions).toBe(subs);
});
});
If you use Jasmine, I wrote a little helper called marbleTest() to reduce boilerplate, available in #s-libs/ng-dev:
import { marbleTest } from "s-ng-dev-utils";
it("generate the stream correctly", marbleTest(helpers => {
const { cold, expectObservable, expectSubscriptions, testScheduler } = helpers;
const e1 = cold(" -a--b--c---|");
const subs = " ^----------!";
const expected = "-a-----c---|";
expectObservable(e1.pipe(throttleTime(3, testScheduler))).toBe(expected);
expectSubscriptions(e1.subscriptions).toBe(subs);
}));
Related
I've updated my cypress to 9.7.0 version and right now I have a problem with deep equal. When I wrote test line of code:
expect([1,2,3]).to.deep.equal([1,2,3]);
Everything works correctly.
While I'm testing redux store I got an error which is looks
Timed out retrying after 4000ms: expected [ Array(2) ] to deeply equal [ Array(2) ]
Arrays in devtool console preview are the same... I've tried in two ways to write test. I also combined it with async and timeouts
First try:
it('Example redux check', () => {
cy.fixture('file.json').then((fixtures) => {
cy.window()
.its('store')
.invoke('getState')
.its('queue.queueItems').should('deep.equal', fixtures.store.queue.queueItems);
});
});
Second try
it('Example redux check', () => {
cy.fixture('file.json').then((fixtures) => {
const getQueueItems = (win) => {
return win.store.getState().queue.queueItems;
}
cy.window()
.pipe(getQueueItems)
.should('deep.equal', fixtures.store.queue.queueItems);
});
});
Had anyone similar issue or idea how to avoid that timeout? Exactly the same is happening while comparing async payloads...
I couldn't fault the deep.equal assertion in Cypress v9.7.0, even deeply nested arrays and objects - except when the order differed.
If your problem is difference in array order, try adding package deepEqualInAnyOrder
const deepEqualInAnyOrder = require('deep-equal-in-any-order');
chai.use(deepEqualInAnyOrder);
it('matches when ordering is different', () => {
const expected = [{a:{x:1}}, {b:2},{c:3}];
expect([{a:{x:1}}, {b:2}, {c:3}]).to.deep.equal(expected) // passes
expect([{b:2}, {a:{x:1}}, {c:3}]).to.deep.equal(expected) // fails
expect([{b:2}, {a:{x:1}}, {c:3}]).to.deep.equalInAnyOrder(expected) // passes
});
I also wanted to see if deep.equal on the <h1> element of http://example.com would succeed.
Here is my minimal, reproducible example.
// Cypress 9.7.0
it('passes deep-equal of two DOM objects', () => {
cy.visit('http://example.com')
cy.get('h1').then($el1 => { // get h1 1st time
cy.get('h1').then($el2 => { // get another copy
// Are they different objects?
expect($el1).to.not.equal($el2) // pass
expect($el1 === $el2).to.equal(false) // pass
// Do they deep-equal
expect($el1).to.deep.equal($el2) // pass
})
})
})
Nearly all frontends I use have Cypress including some open source ones.
It is related to a few issues opened on GitHub and affects latest versions of Cypress.
https://github.com/cypress-io/cypress/issues/21353
https://github.com/cypress-io/cypress/issues/21469
This is what I get on a WIP after upgrading some tests that use deep.equal:
Your first example looks more standard.
I recommend downgrading to a lower version. The following version worked for me before upgrade:
9.5.4
I want to update my UI when a note has finished playing with tone.js
const now = Tone.now()
synth.triggerAttack("G3", now);
synth.triggerRelease("G3", now + 60 / this.tempo)
How can I get a callback or add a listener to the triggerRelease so I can update the UI?
I have never used the library but reviewing its documentation it seems that the API defines an onsilence callback that you could use for your purpose.
You can provide that callback when creating the Synth instance passing the appropriate configuration option:
const synth = new Tone.Synth({
onsilence: () => console.log('I am invoked after the release has finished') // update the UI as appropriate
}).toDestination();
const now = Tone.now()
synth.triggerAttack("G3", now);
synth.triggerRelease(now + 60 / this.tempo);
The API provides some tests related to it as well.
As you indicated in your comments the onsilence callback seems to be invoked only when the instrument is completely in silence, but you need to perform some action when a note ends.
Probably the way to go would be working with the different events offered by the library.
The library documentation provides different related examples that exemplifies how to use them.
Consider for instance review this one and the companion wiki page that try explaining how to sync visuals, or this other one, close to the per note callback you are looking for.
These two examples can be found in the tone.js repository.
With these in mind, consider for instance this code, based on the second example provided when describing the Part event and the snippet of code provided in the aforementioned article about performance and how to sync visuals:
import * as Tone from 'tone';
const synth = new Tone.Synth().toDestination();
// use an array of objects as long as the object has a "time" attribute
const part = new Tone.Part(
(time, value) => {
// the value is an object which contains both the note and the velocity
synth.triggerAttackRelease(value.note, '8n', time);
Tone.Draw.schedule(function () {
//this callback is invoked from a requestAnimationFrame
//and will be invoked close to AudioContext time
document.getElementById('app').innerHTML += ' ' + value.note;
}, time);
},
[
{ time: 0, note: 'C3' },
{ time: '0:2', note: 'C4' },
]
).start(0);
Tone.Transport.start();
Please, reload the preview page in Stackblitz, you should see every note.
Finally, you have several ways if you need to access the parent context this object as indicated in your comments: the exact way will depend on your actual code. Please, consider for instance review this related SO question, I think it could be of help.
Personally I see playwright as a tool that goes into the direction of system/end-to-end tests. So I used playwright + jest to build-up user journeys and integrated them in a CI/CD process.
Since playwright created it's own test runner with useful features like taking videos and trace on failures, it would make sense to switch from jest to #playwright/test. On their home page playwright recommends to use test fixtures so I definitly want to include them in the switch.
In the following I will take amazon as an example.
With playwright + jest the first thing that I did was to create a function for generic setup of the environment:
function setupSuite({ browserOptions, userConfig }){
// create playwright browser and use browserOptions as overrides
// create page from browser
// register self-implemented trace and screenshot reporters to page
// go to baseUrl of current environment (e.g. local, branch etc..)
// click on cookie banner, since it's blocking the UI
// create a user based on userConfig (e.g. user has amazon prime user? payment options? etc.)
// return { browser, page, user, ... }
}
And of course a function to clean up everything again:
function teardownSuite(suite){
// close browser
// delete user
// etc..
}
Then I would use a file for each user journey. In case of amazon a user journey could be the successful processing of an order:
describe("Successful Order", () => {
let suite
beforeAll(async () => {
const userConfig = { isPrime: false, paymentOptions: [ "paypal", "visa" ] }
suite = await setupBrowser({ userConfig })
// I actually extracted that logic in a function to be able to use it in other tests too,
// but just want to make clear whats happening here
const { page, user } = suite
await page.fill(".login-username-input", user.username)
await page.fill(".login-password-input", user.password)
await page.click(".login-submit-button")
})
afterAll(() => teardownSuite(suite))
test("search for toothbrush with suggestions", async () => {
const { page } = suite
await page.fill(".search-input", "tooth")
await page.click("text='toothbrush")
// hit enter
// do some assertions to check if the search was really successful
})
test("click on first item and add to chart", async () => {
// page actions and assertions
})
test("go back, click on second item and add to chart", async () => {
// page actions and assertions
})
test("go to chart and pay", async () => {
// page actions and assertions
})
test("check order confirmation mail", async () => {
// page actions and assertions
})
})
As you can see I split up my test in logical parts to make it more readable and also to see at which step (test block) it failed.
What would be the best way to migrate this to #playwright/test + fixtures?
How would you migrate setupSuite / teardownSuite ? Yes you could use a fixture, but setupSuite expects arguments like the userConfig . Is it possible to have parameterized fixtures?
How would you structure tests with fixtures? If you want to simulate complete user journey the tests are getting bigger than just testing a login for example. A test block would then have a lot of lines without the possibility to structure them.
Is it possible to setup a page so it's shared accross all tests? The beforeAll hook doesn't receive any page and the each test block always receives its own page. This means that there is no connection between test blocks. If you create a page manually in beforeAll and use the same page instance in every test it would probably be a bad practice and video and tracing would probably not work.. so what can be done here?
Are user journey like tests actually bad? I feel like they can't be combined well with the fixture approach of playwright as mentioned in the docs. Fixtures in playwright feel like very data-driven which doesn't really fit to end-to-end testing IMO.
here is what I have for file.js
How could I mock the following and perform a unit testing:
thanks
var dataset = document.getElementById('test') ?
document.getElementById('test').dataset : {};
export const SERVICE_URL = 'http://localhost:/';
Using JEST / EZNYME for REACTJS - something along these lines: file.test.js
const mockTrue = {SERVICE_URL};
describe ( " Service URL ", () =>{
it ( ' should output ', () => {
expect(mockTrue).toBeCalled('http://localhost:xxxxxxx/');
});
Testing dataset
You won't be able to test both branches of the ternary condition with the current implementation.
This is because the value for dataset will be resolved the moment you import it. It is not possible to perform operations later on to add an element with id=test and hope that the value gets updated.
In order to get 100% coverage on this, you have to export a function instead :
export function getDataSet(){
return document.getElementById('test')
? document.getElementById('test').dataset
: {};
}
which makes this testable.
Testing SERVICE_URL
As other fellows mentioned, you can't do any better than
expect(SERVICE_URL).toBe('http://localhost:/');
Testing constant values is just testing for the sake of it. Apart for ensuring this constant doesn't get modified without an "are you sure?" flag, this doesn't bring any value whatsoever.
Some comments
Achieving 100% test coverage is an utopy that lead to the development of brittle tests. This purism is doing you disservice.
If truly you want to get to this shiny 100%, it is preferable that you remove dummy code from test coverage.
Try this, this should assert that your SERVICE_URL contains the value you expect:
describe ( " Service URL ", () =>{
it ( ' should output ', () => {
expect(SERVICE_URL).toBe('http://localhost:/');
});
});
While learning electron, I decided I would also like to train testing techniques in JavaScript. I have following code:
const winston = require('winston');
const AutoLaunch = require('auto-launch');
const launchFunction = (mb) => {
const autolaunch = new AutoLaunch();
autolaunch
.isEnabled()
.then((isEnabled) => {
if (isEnabled) {
return;
}
autolaunch.enable();
})
.catch((err) => {
winston.error(err);
});
};
I would like to assert if autolaunch.enabled() is properly triggered under specific condition and I have a lot of problem with writing any test that won't force me to create stub with exact copy of function from then(). There is an option that maybe something is wrong in this design of this solution - I can (and would like to) change code to make it more testable. How should I cope with that problem without compromising code testability?
I use mocha and sinon but I don't feel really attached to those tools
I would try more functional approach. Wrap closure in question in function and test it separately.
function enableWhenNeeded(autolaunch, isEnabled) {
if (isEnabled) {
return;
}
autolaunch.enable();
}
autolaunch
.isEnabled()
.then(curry(enableWhenNeeded, autolaunch))
.catch((err) => {
winston.error(err);
});
For purpose of the example, I made up function curry(), but I think there are at least thirty five JavaScript frameworks which provide one.
It is always a question is this code worth testing; if AutoLaunch is third party, why testing it?