Flutter Bloc Test verify/running a function after get result - flutter

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 ====================================

Related

While testing error responses, the test fails with the expected error (React/Jest/ReactQuery/Axios/MSW)

I am trying to test error states of the following MSW rest endpoint:
import { rest } from 'msw'
export const exceptionHandlers = [
rest.post(config.accountApiUrl + '/login', (req, res, ctx) => {
return res(
ctx.status(500),
ctx.json({ data: { message: 'Mock Error Message' } })
)
})
]
This endpoint is called in a custom hook return function thats using React Query's mutateAsync:
const { mutateAsync } = useMutation(AuthApi.login)
const handleLogin = async (props): Promise<void> => {
await mutateAsync(props, {
onSuccess: async () => {
// this block tests fine
}
onError: async () => {
console.log('!!!')
// it reaches this block, '!!!' is logged to the console,
// but the test still fails with `Request failed with status code 500`
}
})
}
return handleLogin
In a test file:
it('handles network errors', async () => {
mswServer.use(...exceptionHandlers)
const user = userEvent.setup()
const screen = render(<LoginForm />)
const submitButton = screen.getByTestId('Login.Submit')
// Complete form
await user.click(submitButton)
})
It doesnt matter what comes after that, the test always fails with
Request failed with status code 500
at createError (node_modules/axios/lib/core/createError.js:16:15)
at settle (node_modules/axios/lib/core/settle.js:17:12)
at XMLHttpRequestOverride.onloadend (node_modules/axios/lib/adapters/xhr.js:54:7)
at XMLHttpRequestOverride.trigger (node_modules/#mswjs/interceptors/src/interceptors/XMLHttpRequest/XMLHttpRequestOverride.ts:176:17)
at node_modules/#mswjs/interceptors/src/interceptors/XMLHttpRequest/XMLHttpRequestOverride.ts:354:16
But its supposed to fail with status 500. That's the whole point. If I change the handler to return another error, ie ctx.status(404), then the test just fails with that error code.
I've tried wrapping the assertion in a try/catch block but the same thing results. I see examples online of people doing (apparently) exactly this and it works fine, so I'm quite confused what's causing this. All other tests that check success states work as expected.
i've had the same problem.
As far as i could understand, the problem is that in test environment there is no handler for the rejected promise.
https://github.com/TanStack/query/issues/4109

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,
],
);
},
);

How to handle unexpected asynchronous exceptions with runZonedGuarded?

I follow examples in runZonedGuarded function to setup reporting of unhandled async exceptions.
It works, but every output in console now prints twice:
sso: started silent sign-in
sso: started silent sign-in
sso: no user stored
sso: no user stored
async error
async error
Why it is and how can I fix it?
The code:
runZonedGuarded(
() => runApp(MyApp(config)),
(error, stackTrace) => print('async error'),
);
The first parameter for runZonedGuarded should be async function, not sync.
I added word 'async' and if fixed the issue:
runZonedGuarded(
() async => runApp(MyApp(config)),
(error, stackTrace) => print('async error'),
);

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),
],

Test bloc test with parameter failed

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?