Reputation: 1052
I'm trying to give a value to JsonPlaceholderService().getPlaceholder()
when it's called on button click in the widget test but it fails with
StateError (Bad state: No method stub was called from within `when()`.
Was a real method called, or perhaps an extension method?)
home.dart
import 'package:flutter/material.dart';
import 'package:fluttertest/json_placeholder_service.dart';
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String text = '';
Future<void> _onPressed() async {
final response = await JsonPlaceholderService().getPlaceholder();
setState(() {
text = response.title;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
child: const Text('Get text'),
onPressed: () => _onPressed(),
),
const SizedBox(height: 40),
Text(text),
],
),
),
);
}
}
json_placeholder_model.dart
class JsonPlaceholderModel {
int id;
int userId;
String title;
bool completed;
JsonPlaceholderModel({
required this.id,
required this.userId,
required this.title,
required this.completed,
});
Map<String, dynamic> toJson() => {
'id': id,
'userId': userId,
'title': title,
'completed': completed,
};
JsonPlaceholderModel.fromJson(dynamic json)
: id = json['id'],
userId = json['userId'],
title = json['title'],
completed = json['completed'];
}
json_placeholder_service.dart
import 'dart:convert';
import 'package:fluttertest/json_placeholder_model.dart';
import 'package:http/http.dart' as http;
class JsonPlaceholderService {
Future<JsonPlaceholderModel> getPlaceholder() async {
const url = 'https://jsonplaceholder.typicode.com/todos/1';
final response = await http.get(Uri.parse(url));
final data = jsonDecode(const Utf8Decoder().convert(response.bodyBytes));
final JsonPlaceholderModel json = JsonPlaceholderModel.fromJson(data);
return json;
}
}
widget_test.dart
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:fluttertest/json_placeholder_model.dart';
import 'package:fluttertest/json_placeholder_service.dart';
import 'package:fluttertest/main.dart';
import 'package:mockito/mockito.dart';
void main() {
testWidgets('Should display api text on button click',
(WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(const MyApp());
// Stub http request
final json = {
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
};
final response = JsonPlaceholderModel.fromJson(json);
// Methode 1 -> says to use methode 2
when(JsonPlaceholderService().getPlaceholder())
.thenReturn(Future.value(response));
// Methode 2
when(JsonPlaceholderService().getPlaceholder())
.thenAnswer((_) async => response);
// Click the button
final button = find.byType(ElevatedButton);
expect(button, findsOneWidget);
await tester.tap(button);
await tester.pumpAndSettle();
await tester.pump(const Duration(seconds: 2));
// The text is displayed
expect(find.text('delectus aut autem'), findsOneWidget);
});
}
All this code is available in this repo : https://github.com/TheSmartMonkey/flutter-http-widget-test
Upvotes: 0
Views: 833
Reputation: 1052
First init your service in your widget constructor then it could be mocked
Second mock your service
main.dart
void main() {
runApp(MyApp(client: JsonPlaceholderService()));
}
home.dart
class MyHomePage extends StatefulWidget {
final String title;
final JsonPlaceholderService client;
const MyHomePage({
Key? key,
required this.title,
required this.client,
}) : super(key: key);import 'package:flutter/material.dart';
import 'package:fluttertest/json_placeholder_service.dart';
class MyHomePage extends StatefulWidget {
final String title;
final JsonPlaceholderService client;
const MyHomePage({
Key? key,
required this.title,
required this.client,
}) : super(key: key);
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String text = '';
Future<void> _onPressed() async {
final response = await widget.client.getPlaceholder();
setState(() {
text = response.title;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
child: const Text('Get text'),
onPressed: () => _onPressed(),
),
const SizedBox(height: 40),
Text(text),
],
),
),
);
}
}
widget_test.dart
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:fluttertest/json_placeholder_model.dart';
import 'package:fluttertest/json_placeholder_service.dart';
import 'package:fluttertest/main.dart';
import 'package:mocktail/mocktail.dart';
class MockJsonPlaceholderService extends Mock
implements JsonPlaceholderService {}
void main() {
final mockJsonPlaceholderService = MockJsonPlaceholderService();
testWidgets('Should display api text on button click',
(WidgetTester tester) async {
// Given
// Build our app and trigger a frame.
await tester.pumpWidget(
MyApp(client: mockJsonPlaceholderService),
);
// When
// Stub http request
final json = {
"userId": 1,
"id": 1,
"title": "delectus aut autem",
"completed": false
};
final response = JsonPlaceholderModel.fromJson(json);
when(() => mockJsonPlaceholderService.getPlaceholder())
.thenAnswer((_) async => response);
// Click the button
final button = find.byType(ElevatedButton);
expect(button, findsOneWidget);
await tester.tap(button);
await tester.pumpAndSettle();
await tester.pump(const Duration(seconds: 2));
// Then
verify(() => mockJsonPlaceholderService.getPlaceholder()).called(1);
expect(find.text('delectus aut autem'), findsOneWidget);
});
}
article : https://gist.github.com/brianegan/414f6b369c534a0e5f20bff377823414
All this code is available in this repo : https://github.com/TheSmartMonkey/flutter-http-widget-test
Upvotes: 0
Reputation: 441
This is because you called a real method instead of calling a stub.
To mock a stub, you can use the Mocktail package (I prefer this over mockito
because it is easier to use with null safety) and create a class MockJsonPlaceholderService
that extends Mock
from the mocktail
package.
Then make the JsonPlaceholderService
as a constructor argument of MyApp
(this is known as dependency injection, you may refer to this section of the article for more details).
So that in your test, after you have created an instance of MockJsonPlaceholderService
, you may define a stub method with:
when(() => mockJsonPlaceholderService.getPlaceholder()).thenReturn(yourMockResponse);
Let me know if this helps.
Upvotes: 1