Flutter Testing Redux-Saga with Action Argument

638 Views Asked by At

I'm trying to test a saga where the parameter is an action however I am getting the following error

NoSuchMethodError: Closure call with mismatched arguments: function 'startUp'
Receiver: Closure: ({dynamic action}) => dynamic from Function 'startUp': static.
Tried calling: startUp(Bootstrap)
Found: startUp({dynamic action}) => dynamic

How do I test this saga? The test example below is from the flutter redux saga docs however its not extensive at all

// Action
class Bootstrap {
  final String name;
  Bootstrap (this.name);
}

// Saga
startUp({dynamic action}) sync* {
  // todo api call
  yield Put(Load(action.name));
}


watchSaga() sync* {
  yield TakeLatest(startUp, pattern: Bootstrap);
}


// Test
void main() {
  group('Middleware tests', () {
    test('callApi test', () async {
      var sagaMiddleware = createTestMiddleware();

      var dispatched = [];

      sagaMiddleware.dispatch = (dynamic action) {
        dispatched.add(action);
      };

      sagaMiddleware.getState = () {
        return 'test';
      };

      var task =
          sagaMiddleware.run(startUp, args: [Bootstrap]); // Bootstrap is the action class
      expect(task.toFuture(), completion(equals(0)));
      expect(task.toFuture().then((value) => dispatched),
          completion([TypeMatcher<Load>()]));
    });
  });
}
1

There are 1 best solutions below

0
Bilal Uslu On

You should first run watchSaga. Then you can dispatch actions and it will handle startUp saga run cases. I recommend you to test the saga generator functions for testing.

Check the following link for testing sagas.

https://github.com/reduxsaga/redux_saga/blob/master/doc/advanced/Testing.md

For how to use TakeLatest effect, you can check;

https://pub.dev/documentation/redux_saga/latest/redux_saga/TakeLatest.html

Testing full saga may be a little bit difficult. If so you may need to create a different test infrastructure to handle all (a test redux store, mocking functions, effect middlewares etc.).

For your case it would be like following;

import 'package:redux_saga/redux_saga.dart';
import 'package:test/test.dart';

bool called = false;

void Load(name) async {
  //do something here
  called = true;
}

// Action
class Bootstrap {
  final String name;
  Bootstrap(this.name);
}

// Saga
startUp({dynamic action}) sync* {
  // todo api call
  print('here');
  yield Call(Load, args: [action.name]);
}

watchSaga() sync* {
  yield TakeLatest(startUp, pattern: Bootstrap);
}

void main() {

  group('Middleware tests', () {
    test('callApi test', () async {
      var sagaMiddleware = createTestMiddleware();

      //var dispatched = [];

      // sagaMiddleware.dispatch = (dynamic action) {
      //   print(action);
      //   dispatched.add(action);
      // };

      // sagaMiddleware.getState = () {
      //   return 'test';
      // };

      sagaMiddleware.run(watchSaga);

      called = false;

      var task = sagaMiddleware.run(() sync* {
        yield Put(Bootstrap('load_something'));
      });

      expect(task.toFuture().then((value) => called), completion(equals(true)));
    });
  });
}

Please note, you should not use fake methods here (dispatch and getState) in order to allow TakeLatest to function properly.