Detox: Do you need to call device.reloadReactNative before each test? - detox

It's unclear to me if it is necessary / best practice to call device.reloadReactNative() before a test runs in Detox. The examples make it seem like it is best practice but it's unclear to me.
For example calling it in the beforeAll, is this necessary?
describe('Example', () => {
beforeAll(async () => {
await device.reloadReactNative();
});
it('render a tappable list of posts', async () => {...});
});
I read here that it is best practice to call it but I'm not sure about the reliability of the source.

You need to reset the app state somehow to a familiar baseline, before starting a new test. You can relaunch your app, or even reinstall it, if you must, but those are far slower. Reloading RN is a nice shortcut for RN apps.

Related

How to properly test if a Toast has been shown in react native using native base?

I am trying to write a test that checks if the screen is showing a Toast with an error message. The test passes, but there is a warning:
console.error
Warning: You called act(async () => ...) without await.
This could lead to unexpected testing behaviour, interleaving multiple act calls
and mixing their scopes. You should - await act(async () => ...);
The screen is working fine, I am just learning how to write tests. This is my test:
it('shows error correctly', async () => {
mockAxios.get.mockRejectedValueOnce(new Error('Async error'))
const { queryByText } = renderWithRedux(<DiscoverScreen />)
await waitFor(() => {
expect(queryByText(ErrorMessages.GeneralErrorToast)).not.toBeNull()
})
await waitForElementToBeRemoved(() => queryByText(ErrorMessages.GeneralErrorToast), { timeout: 5000 })
})
What am I not doing right? Definitely there is an issue with react native testing, because there are problems for certain async querying, especially when you have several of them. I found that here: https://github.com/callstack/react-native-testing-library/issues/379#issuecomment-720734366
I am using native base for showing the Toast, which is using Animated I think. Should I use jest.useFakeTimers() and how?
After researching how the Toast in native base works (this could be done when you open the source code in github - https://github.com/GeekyAnts/NativeBase/blob/master/src/basic/ToastContainer.js), I found that it uses Animated.timing.
So I had to find out how to deal with react native animations in tests. That article had a solution that worked for me: https://medium.com/#joncardasis/react-native-how-to-test-components-implementing-animated-with-jest-8cabb5fc2730
After I added the code in my jest setup file, this is how my test looks:
global.withAnimatedTimeTravelEnabled(async () => {
const { queryByText } = renderedComponent
await waitFor(() => {
expect(queryByText(ErrorMessages.GeneralErrorToast)).not.toBeNull()
})
global.timeTravel(Constants.ErrorToastDelay)
expect(queryByText(ErrorMessages.GeneralErrorToast)).toBeNull()
})
It works and now the test passes with no warnings!
One little adjustment in my jest configuration was also missing. I had to add this:
"testEnvironment": "jsdom"
I hope this could help someone else, too!

Jest + puppeteer best architecture practices

I just entered the world of testing with puppeteer and jest, and I was wondering what the best practice was in terms of folder architecture and logic.
I've never done testing before and I think I'm getting a little lost in the different principles and concepts and how it all fits together.
I learned to do my tests based on the page-object model, so I have classes for each of my pages, but also for each of my modules ( or components ). For example, in my application, the header or the login modal are components.
Then I have a test file per page or per component.
(for example the landingPage.tests.js file, which uses the model of the LandingPage class in the LandingPage.js file)
Here is a concrete example:
I have different login cases and I'd like to test them all. For example I want to test to connect with a "normal" user, for which the process is simply login then password. Then I need to test with a user who has activated 2FA, or with a user from a company that uses SSO.
I first thought about putting my different tests in authentication.tests.js, in different describe blocks, thinking it would open a new tab each time, but it doesn't... I use puppeteer in incognito mode to make sure each tab is an isolated session.
So my questions are:
Where is the best place to do these test suites?
Am I supposed to have test files that "describe" the pages ( for example, the button must be present, such text must be here etc) and also have "scenario type" test file ( a set of contextual actions to a user, like for my different login cases) ?
Here is authentication.tests.js, in which I would like to tests all my different ways of logging in :
import HeaderComponent from "../../../pages/components/HeaderComponent";
import AuthenticationComponent from "../../../pages/components/AuthenticationComponent";
import LandingPage from "../../../pages/landing/LandingPage";
import {
JEST_TIMEOUT,
CREDENTIALS
} from "../../../config";
describe('Component:Authentication', () => {
let headerComponent;
let authenticationComponent;
let landingPage;
beforeAll(async () => {
jest.setTimeout(JEST_TIMEOUT);
headerComponent = new HeaderComponent;
authenticationComponent = new AuthenticationComponent;
landingPage = new LandingPage;
});
describe('Normal login ', () => {
it('should click on login and open modal', async () => {
await landingPage.visit();
await headerComponent.isVisible();
await headerComponent.clickOnLogin();
await authenticationComponent.isVisible();
});
it('should type a normal user email adress and validate', async () => {
await authenticationComponent.typeUsername(CREDENTIALS.normal.username);
await authenticationComponent.clickNext();
});
it('should type the correct password and validate', async () => {
await authenticationComponent.typePassword(CREDENTIALS.normal.password);
await authenticationComponent.clickNext();
});
it('should be logged in', async () => {
await waitForText(page, 'body', 'Success !');
});
});
describe('SSO login ', () => {
// todo ...
});
});
Thank you and sorry if it sounds confusing, like I said I'm trying to figure out how it all fits together.
Regarding the folder structure, Jest will find any files according to the match config, basically anything called *.spec.js or *.test.js. Looks like you know that already.
What that means is the folder structure is completely up to you. Some people like to have the tests for components in the same folders as the components themselves. Personally I prefer to have all the tests in one folder as it makes the project look cleaner.
The other benefit of having all the tests in one folder is that you can then start to distinguish between the types of tests. Component tests check that pure components render and operate as expected. You don't need Puppeteer for this, use snapshots if you're in a React app. Puppeteer is good for integration tests that navigate through so-called 'happy paths', login, signup, add to cart etc., using a headless Chromium browser.
To answer the specific problem you have been having with Jest / Puppeteer on a new page for each test:
//keep a reference to the browser
let browser
//keep a reference to the page
let page
// open puppeteer before all tests
beforeAll(async () => {
browser = await puppeteer.launch()
})
// close puppeteer after all tests
afterAll(async () => {
await browser.close()
})
// Get a new page for each test so that we start fresh.
beforeEach(async () => {
page = await browser.newPage()
})
// Remember to close pages after each test.
afterEach(async () => {
await page.close()
})
describe('Counter', () => {
// "it" blocks go here.
})
Hope that helps a bit.

Any way to avoid memory leaks with shallowMount in vue-test-utils?

We have a custom unit testing setup for vue that works on Node, Mocha and jsdom package which simulates browser environment (no webpack, karma). We have wrote about 3k specs already (big app with hundreds of components), and now when mocha is running it becomes slower and slower, and eventually the process just hangs. We thought that maybe there is a memory leak in "jsdom", so we changed it to a "domino" (alternative package), but it still hangs.
We checked the heap memory usage and it just keeps growing (up to 1.5 GB!).
So we think that the problem is with either vue or vue-test-utils. It looks like each time we use mount/shallowMount it needs to be destroyed/unmounted after each test to release memory?
Any ideas? Thanks in advance!
The best way i found is to set the wrapper to null after the test suit
example below using mocha
describe(" View ", () => {
let wrapper;
beforeEach() {
wrapper = mount(Com.Vue, { localVue }) ;
});
after( ()=> {
wrapper = null ;
});
});
It made a huge difference in my case was having memory leaks of about 8gb after running test multiple times now using about 300mb
JavaScript has automatic memory management and garbage collection. If you get rid of all references to a piece of data, the memory will be reclaimed
Hope this helps
Thanks for voting
make sure that you call wrapper.destroy(); in afterEach method and if you use mount or shallowMount in tests bodies call wrapper.destroy(); before mount the new Vue instance.
it works for me.
describe(" View ", () => {
let wrapper;
beforeEach() {
wrapper = mount(Com.Vue, { localVue }) ;
});
afterEach(() => {
wrapper.destroy();
});
});

Redux Thunk vs Making api call in react component

I was wondering if what I've been doing in my ReactNative/Redux application is wrong. This is how I've been handling async actions.
MyComponent.js
componentDidMount() {
fetch('https://www.mywebsite.com')
.then(data => this.props.handleApiSuccess(data)) // injected as props by Redux
.catch(err => this.props.handleApiError(err)); // injected as props by Redux
}
The redux-thunk way I should probably be doing
export const handleApiCall = () => dispatch => {
fetch('https://www.mywebsite.com')
.then(data => dispatch(handleApiSuccess(data)))
.catch(err => dispatch(handleApiError(err)));
}
Is there anything wrong with the way its being done in the first part?
There is nothing wrong in terms of bugs: the code will work and serve its purpose.
But in terms of design it has huge flaw: coupling. Merging your fetching logic inside the Components may cause complications as your app will grow.
What if your way of fetching will change, e.g. you'll decide to communicate with server via websockets? What if your way of handling of server response will change, i.e. handleApiError will have to be replaced with something else? What if you'll decide to reuse same fetching in the different part of your app?
In all these cases you'll have to alter your existing Components which ideally shouldn't be affected by such logic changes.

Reusing authentication state between Jasmine blocks

My app requires users to sign in by submitting a form. I wonder what is the best place to do it in my tests. I came up with some options:
sign-in in beforeEach block (and signout in afterEach block)
sign-in in beforeAll block of every describe (and signout in the last afterAll of every describe)
describe('APP', function () {
describe('FEATURE 1', function () {
beforeAll(function () {
//sign in
});
afterAll(function () {
//sign out
});
//...
});
});
sign-in once for the whole test run in beforeAll of main describe
describe('MY APP', function () {
beforeAll(function () {
//sign in
});
describe('my feature 1', function () {
//...
});
});
Number 1 is the slowest, Number 2 is faster and Number 3 is the fastest, but you are required to have a single entry point for your test runner - not ideal. So which do think is better and why?
It's a matter of different things.
The best practice is closing your browser after EACH test case and it will definitely be the slowest way to run your test. But you will achieve the most clear and the most fair tests. Sure, you can use selenium grid and paralleling your tests and this will be definitely not so long as you expect.
Sometimes you have an application, which logic is not so much dependent on each other and you will probably want to run UI tests into the pre-commit hook for not breaking anything into the branch. Then the approach n.3 will be not so bad (the only thing you should know is what cookies, session vars and other artifacts you should delete in browser after each test).
Anyway that's up to you, but a common and the really BEST practice is opening a clear new browser on every test.