Juste nouveau dans réagir, réagir-redux/saga et plaisanterie
considérer:
----- Le compagnon () ----
componentDidMount() {
this.props.actions.initTodos(
axios,
ajaxURLConstants.WP_GET_TODOS,
appStateActions.setAppInIdle,
appStateActions.setAppInProcessing,
todosActions.todosInitialized
);
}
Ainsi, lorsque mon composant TodoApp a été monté, il envoie l'action INIT_TODOS que mon saga racine écoute et, lorsqu'il le détecte, génère la saga appropriée des travailleurs pour agir en conséquence.
----- La saga des travailleurs correspondants -----
export function* initTodosSaga( action ) {
try {
yield put( action.setAppInProcessing() );
let response = yield call( action.axios.get , action.WP_GET_TODOS );
if ( response.data.status === "success" )
yield put( action.todosInitialized( response.data.todos ) );
else {
console.log( response );
alert( response.data.error_msg );
}
} catch ( error ) {
console.log( "error" , error );
alert( "Failed to load initial data" );
}
yield put( action.setAppInIdle() );
}
----- Le test jusqu'à présent -----
import todos from "../../__fixtures__/todos";
import { initTodosSaga } from "../todosSaga";
test( "saga test" , () => {
let response = {
status : "success",
todos
},
action = {
axios : {
get : function() {
return new Promise( ( resolve , reject ) => {
resolve( response );
} );
}
},
WP_GET_TODOS : "dummy url",
setAppInIdle : jest.fn(),
setAppInProcessing : jest.fn(),
todosInitialized : jest.fn()
};
let initTodosSagaGen = initTodosSaga( action );
initTodosSagaGen.next();
expect( action.setAppInIdle ).toHaveBeenCalled();
} );
----- Le résultat du test -----
Donc, la partie importante est cette
console.error_modules_noeud\redux-saga\lib\internal\utils.js: 240
uncaught at check put (action): l'action argument n'est pas définie
mais j'ai console.log l'action que j'ai transmise lors de mon test dans la saga des travailleurs et, en effet, ce n'est pas indéfini
qu'est-ce que je rate?
Merci d'avance.
----------Mettre à jour------------
Ok remarquez en haut qu'il se plaint de cette ligne de code
yield put( action.setAppInIdle() );
Ce qui est en dehors du bloc try catch, donc j'ai fait quelques changements
1.) J'ai déplacé le code ci-dessus à l'intérieur du bloc try catch, juste après la déclaration else de
if ( response.data.status === "success" )
veuillez vérifier le code initTodosSaga ci-dessus
Puis sur mon test de saga, je teste pour
expect( action.setAppInProcessing ).toHaveBeenCalled();
au lieu de la fonction d'espionnage setAppInIdle
et c'est le résultat du test
donc le test a réussi! mais il se plaint toujours que l'action soit indéfinie
maintenant ce qui est intéressant est si dans mon test de saga, si je teste pour cela maintenant
expect( action.setAppInProcessing ).toHaveBeenCalled();
expect( action.setAppInIdle ).toHaveBeenCalled();
C'est le résultat
alors maintenant, il se plaint toujours de l'action toujours indéfinie (je n'ai pas inclus dans ma capture d'écran, mais toujours comme ci-dessus)
plus la deuxième affirmation que j’ai à propos de la fonction d’espionnage setAppInIdle n’a pas été appelée, mais le setAppInProcessing a été adopté!
J'espère que cette information supplémentaire aide à résoudre cette question.
Il semble très difficile de tester la saga Redux sans l'aide d'une bibliothèque externe.
Pour moi, j'ai utilisé https://github.com/jfairbank/redux-saga-test-plan
Cette bibliothèque est très bonne.
Alors voici mes tests maintenant
(--------------------- Test 1 ---------------------} _
Donc, pour ce test, j’ai transmis à la charge d’action presque tout ce dont la saga a besoin pour fonctionner, ex. axios, fonctions de créateur d’action, etc ............. ressemble plus à suivre le principe de l’injection de dépendance, il est donc facile à tester.
----- Composant TodoApp -----
componentDidMount() {
this.props.actions.initTodos(
axios,
ajaxURLConstants.WP_GET_TODOS,
appStateActions.setAppInIdle,
appStateActions.setAppInProcessing,
todosActions.todosInitialized,
todosActions.todosFailedInit
);
}
Ainsi, lorsque le composant est monté, il déclenche une action que ma saga racine écoute et capture, puis génère la saga appropriée des travailleurs pour agir en conséquence.
remarquez encore une fois que je transmets toutes les données nécessaires dont la saga des travailleurs aurait besoin pour fonctionner correctement dans la charge utile des actions.
_ {----- initTodoSaga (Worker Saga) ----- _
export function* initTodosSaga( action ) {
try {
yield put( action.setAppInProcessing() );
let response = yield call( action.axios.get , action.WP_GET_TODOS );
if ( response.data.status === "success" )
yield put( action.todosInitialized( response.data.todos ) );
else {
console.log( response );
alert( response.data.error_msg );
yield put( action.todosFailedInit( response ) );
}
} catch ( error ) {
console.log( "error" , error );
alert( "Failed to load initial data" );
yield put( action.todosFailedInit( error ) );
}
yield put( action.setAppInIdle() );
}
_ {----- ---- test de saga -----
import { expectSaga } from "redux-saga-test-plan";
import { initTodosSaga } from "../todosSaga";
test( "should initialize the ToDos state via the initTodoSaga" , () => {
let response = {
data : {
status : "success",
todos
}
},
action = {
axios : {
get : function() {
return new Promise( ( resolve , reject ) => {
resolve( response );
} );
}
},
WP_GET_TODOS : "dummy url",
setAppInIdle : appStateActions.setAppInIdle,
setAppInProcessing : appStateActions.setAppInProcessing,
todosInitialized : todosStateActions.todosInitialized,
todosFailedInit : todosStateActions.todosFailedInit
};
// This is the important bit
// These are the assertions
// Basically saying that the actions below inside the put should be dispatched when this saga is executed
return expectSaga( initTodosSaga , action )
.put( appStateActions.setAppInProcessing() )
.put( todosStateActions.todosInitialized( todos ) )
.put( appStateActions.setAppInIdle() )
.run();
} );
et mon test passe yay! :) maintenant pour vous montrer le message d'erreur lorsqu'un test échoue, je commenterai cette ligne de code dans mon initTodosSaga
yield put( action.setAppInIdle() );
alors maintenant l'affirmation
.put( appStateActions.setAppInIdle() )
devrait échouer maintenant
donc il sort (attente non satisfaite} _ ce qui est logique car l'action que nous nous attendions à être renvoyée n'a pas
(--------------------- Test 2 --------------------)
Maintenant, ce test concerne une saga dans laquelle il importe certaines choses dont il a besoin pour fonctionner, contrairement à mon premier test, où je nourris axios, créateurs d’action dans la charge utile
Cette saga a importé axios, des créateurs d’action nécessaires
Heureusement, _ {plan de test de la saga Redux} _ a quelques fonctions d'assistance pour "alimenter" données factices dans la saga
Je vais simplement ignorer le composant qui déclenche l'action que la saga root écoute, ce n'est pas important, je vais simplement coller directement la saga et le test de la saga
---- addTodoSaga ----
/** global ajaxurl */
import axios from "axios";
import { call , put } from "redux-saga/effects";
import * as appStateActions from "../actions/appStateActions";
import * as todosActions from "../actions/todosActions";
export function* addTodoSaga( action ) {
try {
yield put( appStateActions.setAppInProcessing() );
let formData = new FormData;
formData.append( "todo" , JSON.stringify( action.todo ) );
let response = yield call( axios.post , ajaxurl + "?action=wptd_add_todo" , formData );
if ( response.data.status === "success" ) {
yield put( todosActions.todoAdded( action.todo ) );
action.successCallback();
} else {
console.log( response );
alert( response.data.error_msg );
}
} catch ( error ) {
console.log( error );
alert( "Failed to add new todo" );
}
yield put( appStateActions.setAppInIdle() );
}
-----Le test-----
import axios from "axios";
import { expectSaga } from "redux-saga-test-plan";
import * as matchers from "redux-saga-test-plan/matchers";
import * as appStateActions from "../../actions/appStateActions";
import * as todosStateActions from "../../actions/todosActions";
import { addTodoSaga } from "../todosSaga";
test( "should dispatch TODO_ADDED action when adding new todo is successful" , () => {
let response = {
data : { status : "success" }
},
todo = {
id : 1,
completed : false,
title : "Browse 9gag tonight"
},
action = {
todo,
successCallback : jest.fn()
};
// Here are the assertions
return expectSaga( addTodoSaga , action )
.provide( [
[ matchers.call.fn( axios.post ) , response ]
] )
.put( appStateActions.setAppInProcessing() )
.put( todosStateActions.todoAdded( todo ) )
.put( appStateActions.setAppInIdle() )
.run();
} );
Ainsi, la fonction supply vous permet de simuler un appel de fonction tout en fournissant des données factices qu’il doit renvoyer.
et voilà, je peux maintenant tester mes sagas! Yay!
une dernière chose, quand je lance un test pour ma saga qui aboutit à l'exécution d'un code avec un code d'alerte
ex.
alert( "Earth is not flat!" );
Je l'ai sur la console
Error: Not implemented: window.alert
et un tas de traces de pile en dessous, alors peut-être que c'est parce que l'objet d'alerte n'est pas présent sur le noeud? Comment puis-je cacher cela? ajoutez simplement le commentaire si vous avez une réponse.
J'espère que cela aide quelqu'un
Voici une version de travail de votre test:
import todos from '../../__fixtures__/todos';
import { initTodosSaga } from '../todosSaga';
import { put, call } from 'redux-saga/effects';
test('saga test', () => {
const response = {
data: {
status: 'success',
todos
}
};
const action = {
axios: {
get() {}
},
WP_GET_TODOS: 'dummy url',
setAppInIdle: jest.fn().mockReturnValue({ type: 'setAppInIdle' }),
setAppInProcessing: jest.fn().mockReturnValue({ type: 'setAppInProcessing' }),
todosInitialized: jest.fn().mockReturnValue({ type: 'todosInitialized' })
};
let result;
const initTodosSagaGen = initTodosSaga(action);
result = initTodosSagaGen.next();
expect(result.value).toEqual(put(action.setAppInProcessing()));
expect(action.setAppInProcessing).toHaveBeenCalled();
result = initTodosSagaGen.next();
expect(result.value).toEqual(call(action.axios.get, action.WP_GET_TODOS));
result = initTodosSagaGen.next(response);
expect(action.todosInitialized).toHaveBeenCalled();
expect(result.value).toEqual(put(action.todosInitialized(response.data.todos)));
result = initTodosSagaGen.next();
expect(action.setAppInIdle).toHaveBeenCalled();
expect(result.value).toEqual(put(action.setAppInIdle()));
});
Quelques notes:
expect
, je compare le rendement du générateur à ce que le générateur doit faire (c'est-à-dire exécuter les instructions put
et call
)data
était absente de votre réponse fictive