Test bloc test with parameter failed - flutter

So, I have using this bloc_test library version bloc_test: ^5.0.0.
Previously I test bloc with this code and work fine
test(
'should emit [RegisterLoadingState,RegisterLoadedState] when RegisterWithPassword is successful',
() async {
setUpSuccessfulValidateRegister();
setUpSuccessfulRegister();
final expected = [
RegisterInitialState(),
RegisterLoadingState(),
RegisterLoadedState(account: customer),
];
expectLater(bloc, emitsInOrder(expected));
bloc.add(RegisterWithPasswordEvent(
name: nameTest,
email: emailTest,
password: passwordTest,
retypedPassword: retypedPasswordTest,
));
});
Currently I change it with using bloc_test package and the test failed
blocTest(
'should emit [RegisterLoadingState,RegisterLoadedState] when RegisterWithPassword is successful',
build: () async {
setUpSuccessfulValidateRegister();
setUpSuccessfulRegister();
return bloc;
},
act: (bloc) => bloc.add(RegisterWithPasswordEvent(
name: nameTest,
email: emailTest,
password: passwordTest,
retypedPassword: retypedPasswordTest,
)),
expect: [
RegisterLoadingState(),
RegisterLoadedState(account: customer),
],
);
Here is the error in test
Expected: [
RegisterLoadingState:RegisterLoadingState,
RegisterLoadedState:RegisterLoadedState
]
Actual: [
RegisterLoadingState:RegisterLoadingState,
RegisterLoadedState:RegisterLoadedState
]
Which: was RegisterLoadedState:<RegisterLoadedState> instead of RegisterLoadedState:<RegisterLoadedState> at location [1]
package:test_api expect
package:bloc_test/src/bloc_test.dart 124:25 blocTest.<fn>
the error gone if I removed the equatable in state with account parameter. Anyone know why this test works on the first code and failed with bloc_test packages?

Related

Flutter Bloc Test verify/running a function after get result

I try to test bloc (AuthBloc) which running "logout" when getLocalToken is failed and I get this error.
How can I verify/running logout after I get result ServerException on my bloctest.
Please Help.
Thanks before.
My Bloc :
on<AppStarted>((event, emit) async {
final token = await getLocalToken(const Params(needValidation: true));
token.fold(
(failure) {
logout(NoParams());
emit(AuthNotAuthenticated());
},
(auth) => emit(AuthAuthenticated()),
);
});
Here My Test Code :
blocTest<AuthBloc, AuthState>(
'should emit [AuthNotAuthenticated] when token / input is invalid',
build: () {
when(mockGetLocalToken(any))
.thenAnswer((_) async => Left(ServerFailure()));
return bloc;
},
act: (blc) => blc.add(AppStarted()),
expect: () { return <AuthState>[AuthNotAuthenticated()];},
);
Error :
00:03 +2 -1: AppStarted should emit [AuthNotAuthenticated] when token / input is invalid [E]
MissingStubError: 'call'
No stub was found which matches the arguments of this method call:
call(NoParams())
Add a stub for this method using Mockito's 'when' API, or generate the MockLogout mock with a MockSpec with 'returnNullOnMissingStub: true'
Expected: [AuthNotAuthenticated:AuthNotAuthenticated()]
Actual: []
Which: at location [0] is [] which shorter than expected
==== diff ========================================
[[-AuthNotAuthenticated()-]]
==== end diff ====================================

Flutter Cubit Testing States

Trying to write tests for a cubit in flutter. When I run my code, everything works fine, but the tests are causing me issues.
When I run this, I get an error:
> type 'Null' is not a subtype of type 'Future<Fact>'
> MockFactRepository.fetchFact
/test/fact_cubit_test.dart
group(
'Get Fact',
() {
const fact = Fact(text: 'This is a fact');
blocTest(
'emits [FactCubit.initial, .loading, .loaded] when successful',
build: () {
when(mockFactRepository.fetchFact()).thenAnswer((_) async => fact);
return FactCubit(mockFactRepository);
},
act: (FactCubit cubit) => cubit.getFact(),
expect: () => [
FactState.initial().runtimeType,
FactState.loading().runtimeType,
FactState.loaded(fact).runtimeType,
],
);
},
);

Flutter blocTest Missing type arguments for generic function 'blocTest<B extends BlocBase<State>, State>'

When following the bloc test documentation, I created the following blocTest;
blocTest('should do something',
build: () => SignInBloc(signIn: mockSignIn),
act: (bloc) => bloc.add(const OnEmailChanged(email: 'test')));
However I get the intellisense error;
Missing type arguments for generic function 'blocTest<B extends BlocBase<State>, State>'.
And the (bloc) provided in the add is of type Object?.
To fix the issue you have to tell the blocTest what types to expect.
blocTest<SignInBloc, SignInState>('should do something',
build: () => SignInBloc(signIn: mockSignIn),
act: (bloc) => bloc.add(const OnEmailChanged(email: 'test')));
});

Why one of my state is not triggered during my test

I followed Reso code youtube to create a small weather app, but I used the latest bloc library and flutter_bloc 4.0 also.
Base App: https://www.youtube.com/watch?v=hTExlt1nJZI&list=PLB6lc7nQ1n4jCBkrirvVGr5b8rC95VAQ5&index=7
BLoc Test: https://www.youtube.com/watch?v=S6jFBiiP0Mc&list=PLB6lc7nQ1n4jCBkrirvVGr5b8rC95VAQ5&index=8
The first 2 tests are working. For example, the below one does not give me any error:
test(
'NEWER WAY BUT lONG-WINDED emits [WeatherLoading, WeatherLoaded] when successful',
() {
when(mockWeatherRepository.fetchWeather(any))
.thenAnswer((_) async => weather);
final bloc = WeatherBloc(mockWeatherRepository);
bloc.add(GetWeather('London'));
emitsExactly(bloc, [
WeatherInitial(),
WeatherLoading(),
WeatherLoaded(weather),
]);
});
For some reason, that test below does not trigger the WeatherInitial.
blocTest(
'emits [WeatherLoading, WeatherLoaded] when successful',
build: () async {
when(mockWeatherRepository.fetchWeather(any))
.thenAnswer((_) async => weather);
return WeatherBloc(mockWeatherRepository);
},
act: (bloc) => bloc.add(GetWeather('London')),
expect: [
WeatherInitial(),
WeatherLoading(),
WeatherLoaded(weather),
],
);
The error is:
ERROR: Expected: [
WeatherInitial:WeatherInitial,
WeatherLoading:WeatherLoading,
WeatherLoaded:WeatherLoaded
]
Actual: [WeatherLoading:WeatherLoading, WeatherLoaded:WeatherLoaded]
Which: was WeatherLoading:<WeatherLoading> instead of WeatherInitial:<WeatherInitial> at location [0]
Do you have any clue why?
This behaviour is correct according to the official bloc_test library:
skip is an optional int which can be used to skip any number of states.
The default value is 1 which skips the initialState of the bloc.
skip can be overridden to include the initialState by setting skip to 0.
So if you want to include your "InitialState" just ,set the "skip" value to 0 :
.
.
.
skip: 0,
expect: [
WeatherInitial(),
WeatherLoading(),
WeatherLoaded(weather),
],

How to test flutter url_launcher that email app opens?

I do have a Snackbar with a SnackbarAction which should open the default email app with a default subject and body on tap. I am wondering if there is somehow the possibility to verify if this really happens with some unit tests.
My Snackbar code looks like this:
SnackBar get snackbar =>
SnackBar(
content: Text(message),
action: SnackBarAction(
key: const Key('ErrorSnackbarAction'),
label: AppLocalizations
.of(_context)
.report,
onPressed: () async => await launch('mailto:test#test.com?subject=TestSubject&body=TestBody')));
I am already verifying the appearance which works fine:
group('ErrorSnackbar', () {
testWidgets('appearance test', (WidgetTester tester) async {
await tester.pumpWidget(_generateSnackbarApp());
await _showSnackbar(tester);
expect(find.text(userMessage), findsOneWidget);
expect(find.byWidgetPredicate((Widget widget) =>
widget is SnackBarAction && widget.label == 'Report'), findsOneWidget);
});
testWidgets('error report test', (WidgetTester tester) async {
await tester.pumpWidget(_generateSnackbarApp());
await _showSnackbar(tester);
tester.tap(find.byKey(errorSnackbarAction));
await tester.pump();
// how to verify that the default email app was opened
// with expected subject and body?
});
});
Short answer: You can't.
The launch with mailto is handled by the OS of the device and is out of context of the flutter app.
As the flutter test package focuses on the flutter app, what happens on the OS is out of reach.
You can ensure that launchUrl is called and the expected parameters were passed. The url_launcher package should be tested. Therefore, we can expect that the email app opens, when we call launchUrl with the mailto: schema.
Here is a short introduction on how to test the url_launcher package:
Add plugin_platform_interface and url_launcher_platform_interface to your dev dependencies:
dev_dependencies:
flutter_test:
sdk: flutter
plugin_platform_interface: any
url_launcher_platform_interface: any
Copy the mock_url_launcher_platform.dart from the url_launcher package: https://github.com/flutter/plugins/blob/main/packages/url_launcher/url_launcher/test/mocks/mock_url_launcher_platform.dart
Now you can test the launchUrl calls like this:
void main() {
late MockUrlLauncher mock;
setUp(() {
mock = MockUrlLauncher();
UrlLauncherPlatform.instance = mock;
});
group('$Link', () {
testWidgets('calls url_launcher for external URLs with blank target',
(WidgetTester tester) async {
FollowLink? followLink;
await tester.pumpWidget(Link(
uri: Uri.parse('http://example.com/foobar'),
target: LinkTarget.blank,
builder: (BuildContext context, FollowLink? followLink2) {
followLink = followLink2;
return Container();
},
));
mock
..setLaunchExpectations(
url: 'http://example.com/foobar',
useSafariVC: false,
useWebView: false,
universalLinksOnly: false,
enableJavaScript: true,
enableDomStorage: true,
headers: <String, String>{},
webOnlyWindowName: null,
)
..setResponse(true);
await followLink!();
expect(mock.canLaunchCalled, isTrue);
expect(mock.launchCalled, isTrue);
});
});
}
Copied the test from https://github.com/flutter/plugins/blob/main/packages/url_launcher/url_launcher/test/link_test.dart
Unit test wise
I think the best way to test it is to wrap url launcher with your own class.
This way you can mock it and check that your logic calls your wrapper class function to launch the mail app. This should be enough.
As for the actual mail app or browser (in my case) you should trust the library todo what it supposed to so no need to unit test it really.