I'm testing a utility function that returns an integer, I'm trying to simulate calling it, but I can't find the right way to do so even after hours of googling.
I've also tried spyOn() and it didn't seem to work.
Authentication.js
export function auth(username) {
AsyncStorage.getItem('#app:id').then((id) => {
if (id === username) {
return 1;
}
return 0;
});
}
Authentication.test.js
import 'react-native';
import React from 'react';
import renderer from 'react-test-renderer'; // Note: test renderer must be required after react-native.
import mockAxios from 'axios';
import mockAsyncStorage from '#react-native-community/async-storage';
import auth from '../App/Utils/Authorization';
test('should check whether the user whose username is entered as a paramter is the same as the user logged in the application', () => {
auth = jest.fn();
expect(auth).toHaveReturned();
expect(mockAsyncStorage.getItem).toHaveBeenCalledTimes(1);
expect(mockAsyncStorage.multiRemove).toHaveBeenCalledWith('#app:id');
});
I expected a simulation of calling auth() and a successful test, instead, I'm getting an error "auth" is read-only as an output whenever running yarn test.
You are reassigning the imported member auth and not using jest.fn() in the way that is supposed to work. Calling the jest mock function would return undefined, instead with mockFn.mockImplementation(fn) you could bind your function to the mock and test that it's being called or returning some expecting value.
import auth from '../App/Utils/Authorization';
test('Test auth', () => {
const mockAuth = jest.fn().mockImplementation(auth);
mockAuth();
expect(mockAuth).toHaveBeenCalled();
}
You can validate the output of your function checking mockFn.mock.results, which stores the results of every call made over your call function.
test('when user is provided', () => {
mockAuth({ user: {id: 'test', name: 'test'} });
expect(mockAuth).toHaveBeenCalled();
const result = mockAuth.mock.results[0].value;
expect(result).toBe(1);
});
Related
I am trying to retrieve the API_URL from AsyncStorage and make it accessible in all app, the storing and retrieving (in settings screen) is working fine but when I try to load the data in the App.js using useEffect hook, it returns null. Reloading the app is not working but as soon as I save the App.js (using CTRL-S) it works fine.
Please let me know the correct way to do this.
import React, { useEffect, useState } from "react";
import AsyncStorage from "#react-native-async-storage/async-storage";
export default function App() {
const [hostState, setHostState] = useState(null);
const getAHostInfoAsync = async () => {
const hostInfo = AsyncStorage.getItem('host').then(
setHostState(hostInfo)
).then(
console.log(hostState)
);
};
useEffect(() => {
getAHostInfoAsync();
}, []);
module.exports = {
host: hostState
};
}
and using in another file:
import App from "../../../App";
const API_URL = App.host;
I think your issue is in the way you use async/then. instead of async await.
I am not 100% sure that this is your issue. But if I change my async/await function to use async/then the way you are having it, my IDE says that the variable (hostInfo) might not have been initialised. In any case, I think this is a better syntax than with then.
const getAHostInfoAsync = async () => {
const hostInfo = await AsyncStorage.getItem('host')
setHostState(hostInfo)
console.log(hostState)
};
I'm new to jest and trying to test my React Native app. I have what seems like a very simple case: a function called getPastWeek in my AppProvider which I want to test by passing it an argument then checking the return value. However, I am struggling to import the getPastWeek function into my test file:
context.js
const AppProvider = ({children}) => {
const getPastWeek = (param) => {
return param + 5;
}
return (<AppContext.Provider value={{getPastWeek}}>{children}</AppContext.Provider>)
}
export const useGlobalContext = () => useContext(AppContext)
export {AppProvider, AppContext}
context.test.tsx
const { getPastWeek } = useGlobalContext();
it("Test description", () => {
expect(
getPastWeek([{ data: () => ({ date: new Date() }) }]).toBe(true)
);
});
However, this does not work since useGlobalContext needs to be called from within a AppProvider. How can I achieve this desired behaviour and test the getPastWeek function?
EDIT: After doing some digging it seems like one way to achieve this would be to create a test component wrapped in the AppProvider in the test file. However, this seems like overkill to test a single function. Is there a simpler way to do this?
I guess I could also export the function but it seems a little messy to do so
I am new to React Native Programming. So, please tell me in detail. thank you.
calling use Selector
I am calling use Selector inside my functional component like this:
import { useDispatch, useSelector } from 'react-redux';
const AddAddressScreen = ({ navigation }) => {
const dispatch = useDispatch();
const data = useSelector(state => state);
console.log(data + "happy Coding");
return (
<View style={styles.container}>
<View>
);
}
export default AddAddressScreen;
My reducer looks like this
case types.API_LOGIN_SUCCESS:
if (action.result.result.mobile_verified === false) {
return {
...state,
onLoad: false,
result: action.result,
status: action.status,
error: null,
navigation: action.navigation.navigate("VerifyMNO")
};
} else {
return {
...state,
onLoad: false,
result: action.result,
status: action.status,
error: null,
navigation: action.navigation.navigate("AddAddress")
};
}
here my mobile number is verified so I move to the address screen.
where I use Use Selector which gives me an error. while I remove above two lines my code runs successfully.
My saga looks like this
export function* watchLoginUserInfo() {
yield takeLatest(types.LOGIN_USER, loginApiSaga)
}
My root saga
import { all, fork } from 'redux-saga/effects';
import { watchLoginUserInfo, } from './authenticationSagas';
function* rootSaga() {
yield all([
watchLoginUserInfo(),
])
}
export default rootSaga;
My Store looks like this
import {createStore, applyMiddleware} from 'redux';
import rootReducer from '../redux/reducers/root-reducer.js'
import createSagaMiddleware from 'redux-saga';
import rootSaga from '../redux/sagas/rootSaga';
const sagaMiddleware = createSagaMiddleware();
const store = createStore(rootReducer, applyMiddleware(sagaMiddleware));
sagaMiddleware.run(rootSaga);
export {store};
when ever I use use Selector hook in my code it gives me the following error.
error 1
error 2, 3, 4
Use the select effect from redux-saga inside of a reducer: https://redux-saga.js.org/docs/api/#selectselector-args
For example const selectedState = yield select(state => state);.
The useSelector hook is for use inside of a function component.
EDIT: since the above doesn't seem to be the issue, I think the issue is that you're calling navigation functions from within your reducer. Reducer code can have no side effects, so you can't call navigation.navigate(...) from within the reducer. This will need to happen in the saga code instead. It might be able to be done in the loginApiSaga or in a dedicated saga that is triggered by API_LOGIN_SUCCESS.
I am trying to determine if my button is disabled but the disabled property keeps returning undefined. I have checked and followed the instruction in https://logaretm.github.io/vee-validate/advanced/testing.html#testing-validationobserver-debounced-state but it does not work accordingly. I think it is the way I implemented the Jest mock timers. Below is my code
import { mount, createLocalVue } from "#vue/test-utils";
import { ValidationObserver, ValidationProvider } from "vee-validate";
import { BootstrapVue } from "bootstrap-vue";
import Login from "#/pages/login.vue";
const localVue = createLocalVue();
localVue.component("ValidationObserver", ValidationObserver);
localVue.component("ValidationProvider", ValidationProvider);
localVue.use(BootstrapVue);
jest.useFakeTimers();
describe("Login", () => {
test("if username is not entered", () => {
const wrapper = mount(Login, { localVue });
const username = wrapper.find("#username");
const password = wrapper.find("#password");
const login = wrapper.find("#login");
jest.advanceTimersByTime(50);
username.element.value = "test";
password.element.value = "test";
expect(login.attributes("disabled")).toBe(true);
});
});
Basically, the behaviour of my login form is.. The login button is disabled until the user enters a value on the username and password inputs.
I am also using NuxtJS for this
You would still need to wait for pending promises as well as using flush-promises. The docs suggest doing both as a part of a custom flush method.
async function flush() {
await flushPromises();
jest.runAllTimers();
}
So, I am working on a pretty straight forward mobile app that has these scenes:
a list of people
person profile
add form
now, what I do, when I first load the LIST scene, I make an API call (I have a list component that I populate once I get results from the API... state.people).
All good here... when I tap on a person he's profile opens, no extra API calls, just passing the person object from state.people array.
All good here as well.
When I open ADD NEW person and send the form I make another API call (I post the information and get the new Object back)...
now the bit that is confusing to me.
What I would like is to update the LIST scene state.people by making another API call (get all again) after I get the OK confirmation from the POST.
and then navigate to Person's profile.
but, I am outside the scope of the LIST scene (I am in ADD NEW form). So, what would be the correct redux logic for this one?
The LIST component is already mounted... how do I communicate to LIST if I am on different scene
all these binding actions to components properties is confusing too... why can't redux act like a global hub that would always be accessible and would always retain it's state (at least on mobile app)
There is really a lack of real app examples... so far I see only very simplified examples that are not very useful on the grand scale to understand the whole flow
the store I have
/**
* STORE
*/
'use strict';
import { createStore, applyMiddleware } from 'redux';
import reducer from './_reducer';
import promiseMiddleware from 'redux-promise-middleware';
import thunkMiddleware from 'redux-thunk';
const store = createStore(reducer, {}, applyMiddleware(
thunkMiddleware,
promiseMiddleware()
));
export default store;
and the actions I have:
import * as constants from '../../constants/constants';
import request from '../../utils/request';
export const getAll = () => ({
type: constants.PEOPLE_FETCH,
payload: request(constants.API_PATH + 'person', {method: 'GET'})
});
export const search = (data, searchTerm) => ({
type: constants.PEOPLE_SEARCH,
payload: _filter(data, searchTerm)
});
export const save = (data) => ({
type: constants.PERSON_SAVE,
payload: request(constants.API_PATH + 'person', {method: 'POST', body: JSON.stringify(data)})
});
This can be an example architecture for your app:
Make a Redux store with list of people.
On initial API call, update the store to contain the list fetched by API call.
Wrap your app inside Provider and pass the store to the Provider.
Use connect and mapStateToProps and mapDispatchToProps to connect the Redux store to React state.
Whenever you update or insert new person, and get the new object, you need to dispatch an action which then goes to the reducer function which finally returns the updated Redux store, and dont worry with the re-rendering as React does the re-rendering itself whenever there is a change in a state.
I'll give a small example of store/actions/reducer, with a react + redux app.
store.js
import { applyMiddleware, compose, createStore } from 'redux'
import reducer from './reducer'
import logger from 'redux-logger'
// TOOD: add middleware
let finalCreateStore = compose(
applyMiddleware(logger())
)(createStore)
export default function configureStore (initialState = { todos: [] }) {
return finalCreateStore(reducer, initialState)
}
actions.js
let actions = {
helloWorld: function(data){
return {
type: 'HELLO_WORLD',
data: data
}
}
};
export default actions
reducer.js // Please read from Redux docs that reducers need to be pure functions
export default function myReducer(state = [], action) {
switch (action.type) {
case 'HELLO_WORLD':
return 'welcome' + data;
default:
return state;
}
}
Component.js (the React App) //In component whenever you receive new object, dispatch an action which will modify the store.
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import actions from '../redux/actions'
class App extends Component {
handleClick() {
store.dispath(action.helloWorld("jimmy")); //this dispatches an action, which goes to the reducer to change the state and adds 'welcome' before 'jimmy'
}
render() {
return (
<div onClick={this.handleClick.bind(this)}>
{store.getState()} //getState function to access store values
</div>
)
}
}
function mapStateToProps(state) {
return state
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(actions, dispatch) //binds all the actions with dispatcher and returns them
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App)
This works like whenever you click the 'div' in the React Component, it calls the function, handleClick(), in which there is an action dispatch. This action then calls the reducer itself to update the store. I know you might get confused that how is store getting updated. Its a bit confusing but for that you need to follow a basic tutorial to explain React+Redux.
Please note this is not a runnable example, just a pseudocode. I recommend you to watch this youtube series to completely understand the redux stores+ react+webpack