我正在编写 Flutter 测试,尝试模拟一种计算 BMI 的方法。尽管使用 when(...).thenReturn(...) 返回未来值,但我还是遇到了以下错误:EXC...
我正在编写 Flutter 测试,尝试模拟一种计算 BMI 的方法。尽管使用 when(...).thenReturn(...) 返回未来值,但我仍遇到以下错误:
EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK The following _TypeError was thrown running a test: type 'Null' is not a subtype of type 'Future<String>'
怎么运行的
-
p2
- The user opens the app and enters their weight and height in the provided text fields.
- The user presses the "Calculate BMI" button.
-
p3
- The app sends a request to the backend API with the weight and height as parameters.
- The app uses the Provider package to manage state and the ApiService to handle the HTTP request.
-
p4
- The Spring Boot application receives the request and processes the BMI calculation.
- The result is sent back to the app as a response.
-
p5
- The app displays the BMI result and changes the text color based on the BMI category.
这是我的代码的相关部分:
widget_测试.dart
// Define a mock class for ApiService using Mockito
class MockApiService extends Mock implements ApiService {}
void main() {
testWidgets('BMI Calculator Test', (WidgetTester tester) async {
final MockApiService mockApiService = MockApiService();
// Mock response for calculateBMI method
when(mockApiService.calculateBMI(88, 186)).thenReturn(Future.value('22.2'));
await tester.pumpWidget(
ChangeNotifierProvider(
create: (context) => mockApiService,
child: MaterialApp(
home: BmiCalculatorApp(),
),
),
);
// Verify if 'Calculate BMI' button is found on the screen
expect(find.text('Calculate BMI'), findsOneWidget);
// Find the ElevatedButton widget with 'Calculate BMI' text and tap it
await tester.tap(find.widgetWithText(ElevatedButton, 'Calculate BMI'));
await tester.pump();
// Wait for the async operation to complete
await tester.pump(Duration(seconds: 1));
// Verify if 'BMI result:' text is found exactly once on the screen
expect(find.text('Your BMI is 22.2'), findsOneWidget);
});
}
api_服务.dart
import 'package:mockito/mockito.dart';
import 'package:flutter_test/flutter_test.dart';
// Define a service class
class ApiService {
Future<String> calculateBMI(int weight, int height) async {
// Implementation that calculates BMI
return ((weight / (height / 100 * height / 100)).toStringAsFixed(1));
}
}
// Define a mock class
class MockApiService extends Mock implements ApiService {}
void main() {
// Create an instance of the mock class
final mockApiService = MockApiService();
// Set up the mock to return a specific value
when(mockApiService.calculateBMI(88, 186)).thenReturn(Future.value('22.2'));
// Test case
test('Calculate BMI', () async {
final result = await mockApiService.calculateBMI(88, 186);
expect(result, '22.2');
});
}
主程序
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'api_service.dart'; // Import the ApiService class
void main() {
runApp(
// This is the entry point of my Flutter application.
ChangeNotifierProvider( // This widget is used to provide a ChangeNotifier to its descendants. ChangeNotifierProvider: Listens for changes in the information and updates the app automatically.
create: (context) => ApiService(baseUrl: 'http://localhost:8080'), // Create an instance of ApiService with a base URL.
child: MaterialApp( // A widget that configures the MaterialApp.
home: BmiCalculatorApp(), // Set BmiCalculatorApp as the home screen of the app.
),
),
);
}
class BmiCalculatorApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold( // Scaffold widget provides a standard app layout structure.
appBar: AppBar( // AppBar is the top bar of the app that typically contains the title and actions.
title: Text('BMI Calculator'), // Title of the AppBar.
),
body: BmiCalculator(), // The main content of the app is BmiCalculator widget.
);
}
}
class BmiCalculator extends StatefulWidget {
@override
_BmiCalculatorState createState() => _BmiCalculatorState();
}
class _BmiCalculatorState extends State<BmiCalculator> {
final TextEditingController _weightController = TextEditingController(); // Controller to manage weight input.
final TextEditingController _heightController = TextEditingController(); // Controller to manage height input.
String _message = "Please enter your height and weight"; // Initial message displayed to the user.
Color _messageColor = Colors.black; // Color of the message text.
@override
void initState() {
super.initState(); // super.initState() This method call ensures that the initialization process of the parent class (State) is executed. The parent class State may have some essential setup that needs to be done for your widget to function correctly.
// No need to initialize ApiService here anymore
}
void _calculateBMI() async {
double weight = double.parse(_weightController.text); // Get weight value from TextField.
double height = double.parse(_heightController.text); // Get height value from TextField.
try {
// Use Provider.of to get ApiService instance.
final ApiService apiService = Provider.of<ApiService>(context, listen: false); // Get ApiService instance.
String result = await apiService.calculateBMI(weight, height); // Call calculateBMI method from ApiService.
// Extract the BMI value from the result string.
double bmi = double.parse(result.split(' ').last);
// Determine the color based on the BMI value.
if (bmi < 18.5) {
_messageColor = Colors.orange; // Underweight
} else if (bmi < 25) {
_messageColor = Colors.green; // Healthy Weight
} else if (bmi < 30) {
_messageColor = Colors.red; // Overweight
} else {
_messageColor = Colors.grey; // Obese
}
setState(() {
_message = result; // Update _message with the result.
});
} catch (e) {
setState(() {
_message = 'Error: $e'; // If an error occurs, update _message with error message.
_messageColor = Colors.black; // Set color to black for error message.
});
}
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(16.0), // Padding around the entire Column widget.
child: Column( // Column widget to arrange children widgets vertically.
crossAxisAlignment: CrossAxisAlignment.stretch, // Stretch widgets horizontally.
children: <Widget>[
TextField( // TextField widget for weight input.
controller: _weightController, // Controller to manage text input.
decoration: InputDecoration( // Decoration for TextField.
labelText: 'Weight (kg)', // Label text for TextField.
),
keyboardType: TextInputType.number, // Allow numeric input.
),
TextField( // TextField widget for height input.
controller: _heightController, // Controller to manage text input.
decoration: InputDecoration( // Decoration for TextField.
labelText: 'Height (cm)', // Label text for TextField.
),
keyboardType: TextInputType.number, // Allow numeric input.
),
SizedBox(height: 20), // Empty space with a height of 20 pixels.
ElevatedButton( // ElevatedButton widget for BMI calculation.
onPressed: _calculateBMI, // Function to execute when button is pressed.
child: Text('Calculate BMI'), // Text displayed on the button.
),
SizedBox(height: 20), // Empty space with a height of 20 pixels.
Text( // Text widget to display BMI calculation result or error message.
_message, // Text to display.
textAlign: TextAlign.center, // Center-align the text.
style: TextStyle(fontSize: 24, color: _messageColor), // Font size and color of the text.
),
],
),
);
}
}
pubspec.yaml
remove all comments:
name: bmi_calculator
description: "A new Flutter project."
version: 1.0.0+1
environment:
sdk: '>=3.4.3 <4.0.0'
dependencies:
flutter:
sdk: flutter
http: ^0.13.3
provider: ^5.0.0
cupertino_icons: ^1.0.6
dev_dependencies:
flutter_test:
sdk: flutter
mockito: ^5.0.7
flutter_lints: ^3.0.0
uses-material-design: true
我尝试过的:
-
p11
when(mockApiService.calculateBMI(88, 186)).thenAnswer((_) async => '22.2');
-
p12
when(mockApiService.calculateBMI(any, any)).thenReturn(Future<String>.value('22.2'));
-
p13
when(mockApiService.calculateBMI(88, 186)).thenAnswer((invocation) async { final int weight = invocation.positionalArguments[0]; final int height = invocation.positionalArguments[1]; return '22.2';});
问题:
Despite these attempts, I am still encountering the type 'Null' is not a subtype of type 'Future<String>' error.My main.dart
问题:
How can I resolve this TypeError and correctly mock the calculateBMI method to return the expected future value?
我的 Flutter 程序运行完美,但似乎无法通过 widget_test.dart