I've been attempting to write unit tests for a registration page on a new react application
However I am very new to the concept of Sinon stubs/spies and have been having issues with intercepting a function call and forcing a resolve.
This is my initial test:
test('Success notification is displayed if all information is valid', () => {
wrapper.setState({ username: 'test', password: 'Test1234!#', confirmPassword: 'Test1234!#' });
const stub = sinon.stub(Register.prototype, 'signUp').resolves('Test');
wrapper.find('#submitRegistration').simulate('click');
});
onClick runs this event handler: (Simplified)
public onSubmitHandler(e: React.FormEvent) {
// Perform password match check
if (password === confirmPassword) {
this.signUp(this.state.username, password);
} else {
// Set success state
}
} else {
// Set error state
}
}
And finally, Signup:
public signUp(username: string, password: string) {
// Perform Cognito Signup
return Auth.signUp({
password,
username,
})
.then(() => {
// Set success State
})
.catch((err) => {
// Set error state
});
}
How can I intercept the call to signUp and force it down the resolve path, Currently due to the fact i do not configure my AWS Amplify Auth module it catches every time with "No userPool"
Jest provides many ways of mocking out dependencies, primarily there are 2 flavours:
Manual Mocks - These let you control mocks in far more details.
Mock functions - If you need a simple mock for a common problem, translation for example, these are very helpful.
For your case, you will also need to handle Promises, for this reason, you need to follow the advice on testing async behaviour.
For your case, I am assuming:
Auth is a module named 'auth-module'
you just need to setup the mock once and have 1 test data which resolves to success and 1 for failure.
// telling jest to mock the entire module
jest.mock("auth-module", () => ({
// telling jest which method to mock
signUp: ({ password, username }) =>
new Promise((resolve, reject) => {
// setting the test expectations / test data
process.nextTick(
() =>
username === "SuccessUser"
? resolve(userName)
: reject({
error: "failure message"
})
);
})
}));
Note : Adding the snippet even though it will not run, as the formatting seems to be broken with code block.
With a little thought push from dubes' answer I managed to get the following:
const signUpSpy = jest.spyOn(Auth, 'signUp').mockImplementation((user, password) => {
return new Promise((res, rej) => {
res();
});
});
Instead of spying directly onto the function I wrote and call moved it to the Modules function and resolved the promise from there!
Related
The problem:
Nothing happends when throwing throw redirect(302, '/auth/sign-up-success') in SvelteKit's actions if onSuccess: () => {...} is set in Felte's createForm({...}).
Example:
// +page.server.ts
export const actions: Actions = {
default: async (event) => {
...
throw redirect(302, '/auth/sign-up-success');
}
}
// SignUpForm.svelte
const { form, errors } = createForm({
onSuccess: (response) => {
invalidate('app:auth')
},
...
}
If I would delete the onSuccess part, then redirect would happend.
Question:
Is there a way to reuse that redirect form success response logic from default Felte form config without writing it again myself?
Action responses are JSON objects with a type, you could read the response and redirect on the client:
async onSuccess(response) {
const { type, location } = await response.json();
if (type == 'redirect') {
goto(location); // from '$app/navigation'
return;
}
}
I would not recommend using this library though. It appears to be incompatible with SSR and one of its main actions shares the name of the form data property used by SvelteKit form actions.
Depending on why you are using this, there might be more suitable tools for SvelteKit in particular (if you even need any, SvelteKit does many things out of the box).
I have the following code
getFullName() {
var urlendPoint = new URL(propertiesInFile.urltoPing)
var params = {
firstName: this.state.firstName,
lastName: this.state.lastName
}
urlendPoint.search = new URLSearchParams(params)
fetch(urlendPoint).then(response => response.json()).then((data) => {
...perform some actions...
}
})
.catch(error => {
alert("Please check the names and try again");
});
}
The propertiesInFile are just hard coded values that I have defined in another js file.
In the above code, I can't seem to mock the fetch call. How do I test this function in react such that I can get the states (of firstname and lastName) in the function, cover the catch block and also try the fetch call that is in place.
In your test you can assign fetch to a mock function something like this
global.fetch = jest.fn().mockImplementation(() => mockFetchPromise);
You can find more details on this website
I am trying to add a unit test to validate the Yup.isValid function, but the after running test case showing error as: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL. Even if I am changing the minimum timeout of jasmine same error showing. My function to validate the Yup schema is:
export const validateSchema= (
validationSchema,
data
) => {
return new Promise(async (resolve, reject) => {
await validationSchema
isValid(data)
.then(isFormValid => {
//passing response to method
})
.catch(error => reject(error));
});
};
My test case is:
test("validate Schema",async () => {
let catchFn = jest.fn();
let data= someSampleData;
//data is valid as per the schema
await validationSchema(
validationSchema,
data
)
.then(res => {
//My expected condition
})
.catch(catchFn);
});
Above test case is not going to then where I can put my condition. Same error is coming as I mentioned. How can I fix this issue?
For large schemas it might be nice to use yup's validateAt api to pass in a path to validate for, so fixture data can be more concise.
Jest specs could look something like:
await expect(schema.validateAt(path, fixture)).rejects.toBeTruthy()
it("requires person's name", async () => {
await expect(schema.validateAt('person.name', {})).rejects.toBeFalsy()
await expect(schema.validateAt('person.name', {person: {name: "something"}})).resolves.toBeTruthy()
}
Unit testing yup is interesting, in that a lot of it is configuration, testing that libraries work the way they say they do can be extraneous. However, this does seem to helpful for testing more complex custom schema validation behaviors.
validateSchema uses promise construction antipattern and shows one of the reasons why it's considered an antipattern, new Promise is unneeded construction that is prone to human errors.
The use of async as Promise executor is a mistake that contributes to the antipattern. Promise executor ignores a promise that is returned from async function. resolve is never called, while .catch(error => reject(error)) is no-op. validateSchema returns either rejected or pending promise. If pending promise is returned from a test, this results in a timeout.
It should be:
export const validateSchema= async (
validationSchema,
data
) => {
await validationSchema;
const isFormValid = isValid(data);
await updateFormValidFlag(isFormValid, theFormName); // does it return a promise?
});
};
Mixing await and raw promises is rarely ever needed. Using dummy function in catch in test will result in supressing errors, which is rarely a desirable behaviour.
The test can be:
test("validate Schema",async () => {
let data= someSampleData;
await validateSchema(...);
//My expected condition
});
Just another example, I am mainly check for true values
it( 'should handle validating object', async () => {
const result = await schema.isValid(myObject)
expect( result ).toEqual( true )
} )
I am testing API for User functionality (signup and login) using jest.
Code for testing:
const request = require('supertest');
const app = require('../../app');
describe('Test User Functionality', () => {
test('User should be able to login', async done => {
const response = await request(app)
.post('/api/users/login')
.send({
email: 'test#test.com',
password: 'welcome1',
})
.expect(200);
done();
//expect(response.statusCode).toBe(200);
});
test('User should be able to signup', async done => {
const response = await request(app)
.post('/api/users/signup')
.send({
username: 'testuser',
email: 'test#test1.com',
password: 'welcome1',
})
.expect(200);
done();
//expect(response.statusCode).toBe(200);
});
});
In case I have single test, it is working fine but with multiple test inside describe, it is showing timeout errors.
Below is the screenshot of the error:
I tried adding timeout, swapping the test but still no success.
Anyone please help !
I've encountered same issue when testing my react component with async requests.
This happens because you didn't finished your request properly.
You can easily solve this.
Option 1: move the done as the second parameter for expect function call like below.
const response = await request(app)
.post('/api/users/signup')
.send({
username: 'testuser',
email: 'test#test1.com',
password: 'welcome1',
})
.expect(200, done);
Option2: use end method
const response = await request(app)
.post('/api/users/signup')
.send({
username: 'testuser',
email: 'test#test1.com',
password: 'welcome1',
})
.expect(200)
.end((err, res) => {
// do anything you want!
})
Or you can checkout the document(https://github.com/visionmedia/supertest#readme)!
One possible issue might be with express middlewares.
To find out if this is your case, you could:
Comment out all the middlewares for your express app (app.use(/* middleware */) docs)
See if the tests start progressing somehow (pass/not time out)
Start uncommenting the middlewares, to narrow it down to the culprit.
Once you've found where the cause of timeout comes from, you can go deeper.
Mock different parts of the middleware that is causing the issue:
Mock third party libraries by creating a directory __mocks__ in
your root directory and insert a file library_name.js (this is
manual mocking for jest:
documentation)
Mock middlewares and pass right through them by making them just call next app.use(Your.Middleware) and in your middleware file
/** Possibly imports and some setup here */
// Make sure the function being called is mocked
export default {
Middleware: (req, res, next) => {
next();
},
}
(File containing your middleware might contain some additional setup which might cause the issue.)
My particular case:
I was using ioredis in middlewares and in the file on the Redis instantiation it tried to connect to the store multiple times, which caused the time out.
As it turns out the hostname was wrong. The key which gave the issue away was mocking middlewares and finding the extra setup. Then Jest showed another error hinting at store connection problems.
While working on a side project, I faced an issue with react-router-dom.
What I want to implement is: When I submit a Form, I need to save the data on my server. While the request is pending, I need to display a loading indicator. Once the server says everything is ok, I need to redirect the user on a new page
action.js
export const addNotification = value => async dispatch => {
dispatch(addNotificationPending())
try {
const response = await client.createNotification(values)
dispatch(addNotificationSuccess(response))
} catch(e) {
dispatch(addNotificationFailure())
}
}
component.js
class CreateNotificationForm extends Component {
onSubmit = (values) => {
this.props.addNotification(parameters, history)
}
render() {
const { isCreating } = this.props
const submitBtnText = isCreating ? 'Creating...' : 'Submit'
return (
<Form>
// content omitted
<Submit value={submitBtnText} />
</Form>
)
}
}
const mapStateToProps = (state) => ({
isCreating: getIsFetching(state)
})
const mapDispatchToProps = (dispatch) => ({ // omitted })
connect(mapStateToProps, mapDispatchToProps)(CreateNotificationForm)
So far so good: When I submit my form, the form's submit button shows a Creating... text.
However, how do I tell react-router to load a new path once the request is successful?
Right now, I've done that by using withRouter and using this.props.history as a second argument for this.props.addNotification.
It works great, but it seems really wrong
I've seen solutions using react-router-redux, but I don't really want to add a new middleware to my store.
Should I make the API call inside my component and use a Promise?
Any help?
Update:
After working a little on my own React project, and thinking about similar situations where I handle route changes there, I decided I want to change my original answer. I think the callback solution is OK, but the solution that you already mentioned of making the API call inside your component and using a promise is better. I realized that I've actually been doing this in my own app for a while now.
I use redux-form in my app, and it provides onSubmitSuccess/onSubmitFail functions that you can use to handle the submit result, and each of those rely on you returning a promise (usually from your action creator).
I think the fact that one of the most popular packages for form submission in React/Redux supports this pattern is an indication that it's probably a good pattern to use. Also, since react-router passes history into your component, it seems logical that they expect most people to do a lot of their programmatic route changes inside the component.
Here's an example of what the promise solution would look like with your code:
action.js
export const addNotification = value => dispatch => {
return new Promise(async (resolve, reject) => {
dispatch(addNotificationPending())
try {
const response = await client.createNotification(values)
dispatch(addNotificationSuccess(response))
resolve(response)
} catch(e) {
dispatch(addNotificationFailure())
reject(e)
}
})
}
component.js
onSubmit = async () => {
try {
await this.props.addNotification(parameters)
this.props.history.push('/new/route')
} catch(e) {
// could use a try/catch block here to display
// an error to the user here if addNotification fails,
// or go to a different route
}
}
Old Answer:
A simple solution would be to allow addNotification() to accept a callback function as an optional second argument.
export const addNotification = (value, callback=null) => async dispatch => {
dispatch(addNotificationPending())
try {
const response = await client.createNotification(values)
dispatch(addNotificationSuccess(response))
(typeof callback === 'function') && callback()
} catch(e) {
dispatch(addNotificationFailure())
}
}
Then inside your component use the router to go to the new route.
onSubmit = (values) => {
this.props.addNotification(parameters, () => {
this.props.history.push('/new/route')
})
}
You should not write your asynchronous calls in reducers or actions as the documentation clearly suggests them to be pure functions. You will have to introduce a redux-middleware like redux-thunk or redux-saga (I personally prefer sagas)
All your async calls will happen inside the middleware, and when it succeeds, you can use react-routers history .replace() or .push() methods to update your route. Let me know if it makes sense
You can use one popular package axios
See Here https://www.npmjs.com/package/axios
and you can implement your login like
axios.post('/user', {
firstName: 'Fred',
lastName: 'Flintstone'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
You can write your loader login while calling api
and then you can hide your loader in .then