Bird
Raised Fist0
Fluttermobile~20 mins

Mock dependencies in Flutter - Mini App: Build & Ship

Choose your learning style10 modes available
Build: User Profile Screen
This screen shows a user's profile information fetched from a service. We want to build it so that the data fetching service can be replaced with a mock for testing.
Target UI
-------------------------
|      User Profile      |
|-----------------------|
| Name: Loading...       |
| Email: Loading...      |
|                       |
| [Refresh]             |
-------------------------
Create a UserProfileScreen that shows user name and email.
Use a UserService to fetch user data asynchronously.
Inject the UserService dependency so it can be replaced.
Add a Refresh button to reload user data.
Create a mock UserService that returns fixed user data instantly.
Demonstrate switching between real and mock UserService.
Starter Code
Flutter
import 'package:flutter/material.dart';

class User {
  final String name;
  final String email;
  User(this.name, this.email);
}

abstract class UserService {
  Future<User> fetchUser();
}

class RealUserService implements UserService {
  @override
  Future<User> fetchUser() async {
    // Simulate network delay
    await Future.delayed(Duration(seconds: 2));
    return User('Alice', 'alice@example.com');
  }
}

class UserProfileScreen extends StatefulWidget {
  final UserService userService;
  const UserProfileScreen({super.key, required this.userService});

  @override
  State<UserProfileScreen> createState() => _UserProfileScreenState();
}

class _UserProfileScreenState extends State<UserProfileScreen> {
  User? user;
  bool loading = true;

  @override
  void initState() {
    super.initState();
    _loadUser();
  }

  Future<void> _loadUser() async {
    setState(() {
      loading = true;
    });
    // TODO: Use widget.userService to fetch user
    // TODO: Update user and loading state
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('User Profile')),
      body: Center(
        child: loading
            ? CircularProgressIndicator()
            : Column(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Text('Name: ${user?.name ?? ''}'),
                  Text('Email: ${user?.email ?? ''}'),
                  SizedBox(height: 20),
                  ElevatedButton(
                    onPressed: _loadUser,
                    child: Text('Refresh'),
                  ),
                ],
              ),
      ),
    );
  }
}

void main() {
  runApp(MaterialApp(
    home: UserProfileScreen(userService: RealUserService()),
  ));
}
Task 1
Task 2
Task 3
Solution
Flutter
import 'package:flutter/material.dart';

class User {
  final String name;
  final String email;
  User(this.name, this.email);
}

abstract class UserService {
  Future<User> fetchUser();
}

class RealUserService implements UserService {
  @override
  Future<User> fetchUser() async {
    await Future.delayed(Duration(seconds: 2));
    return User('Alice', 'alice@example.com');
  }
}

class MockUserService implements UserService {
  @override
  Future<User> fetchUser() async {
    // Return instantly with fixed data
    return User('Mock Bob', 'mockbob@example.com');
  }
}

class UserProfileScreen extends StatefulWidget {
  final UserService userService;
  const UserProfileScreen({super.key, required this.userService});

  @override
  State<UserProfileScreen> createState() => _UserProfileScreenState();
}

class _UserProfileScreenState extends State<UserProfileScreen> {
  User? user;
  bool loading = true;

  @override
  void initState() {
    super.initState();
    _loadUser();
  }

  Future<void> _loadUser() async {
    setState(() {
      loading = true;
    });
    User fetchedUser = await widget.userService.fetchUser();
    setState(() {
      user = fetchedUser;
      loading = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('User Profile')),
      body: Center(
        child: loading
            ? CircularProgressIndicator()
            : Column(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Text('Name: ${user?.name ?? ''}'),
                  Text('Email: ${user?.email ?? ''}'),
                  SizedBox(height: 20),
                  ElevatedButton(
                    onPressed: _loadUser,
                    child: Text('Refresh'),
                  ),
                ],
              ),
      ),
    );
  }
}

void main() {
  // Switch between RealUserService and MockUserService here
  bool useMock = true;
  runApp(MaterialApp(
    home: UserProfileScreen(
      userService: useMock ? MockUserService() : RealUserService(),
    ),
  ));
}

We created a MockUserService that implements UserService and returns fixed user data immediately. This allows us to test the UI without waiting for network delays.

In UserProfileScreen, we use the injected userService to fetch user data asynchronously in _loadUser(). We update the loading state to show a spinner while fetching.

The main() function demonstrates switching between the real and mock services by toggling the useMock boolean. This shows how dependency injection helps us replace real services with mocks easily.

Final Result
Completed Screen
-------------------------
|      User Profile      |
|-----------------------|
| Name: Mock Bob         |
| Email: mockbob@example.com |
|                       |
| [Refresh]             |
-------------------------
When the screen loads, it shows a spinner for a moment then displays the user name and email from the service.
Pressing the Refresh button reloads the user data and updates the display.
Switching the 'useMock' flag in main() changes the displayed user data between real and mock.
Stretch Goal
Add a toggle switch in the app bar to switch between RealUserService and MockUserService at runtime.
💡 Hint
Use a StatefulWidget to hold the toggle state and rebuild UserProfileScreen with the selected service when toggled.

Practice

(1/5)
1. What is the main purpose of using mocks in Flutter testing?
easy
A. To write less code in the app
B. To speed up the app in production
C. To create UI components faster
D. To replace real services and control their behavior during tests

Solution

  1. Step 1: Understand what mocks do

    Mocks replace real services or dependencies in tests to simulate their behavior.
  2. Step 2: Identify the purpose in testing

    This helps tests run faster and more reliably by controlling external factors.
  3. Final Answer:

    To replace real services and control their behavior during tests -> Option D
  4. Quick Check:

    Mocks replace services = A [OK]
Hint: Mocks simulate services in tests to control behavior [OK]
Common Mistakes:
  • Thinking mocks speed up production app
  • Confusing mocks with UI widgets
  • Believing mocks reduce app code size
2. Which of the following is the correct way to create a mock class using the mocktail package in Flutter?
easy
A. class MockService extends Service implements Mock {}
B. class MockService extends Mock implements Service {}
C. class MockService with Mock implements Service {}
D. class MockService implements Mock, Service {}

Solution

  1. Step 1: Recall mocktail syntax

    In mocktail, a mock class extends Mock and implements the interface to mock.
  2. Step 2: Match syntax to options

    class MockService extends Mock implements Service {} correctly extends Mock and implements Service.
  3. Final Answer:

    class MockService extends Mock implements Service {} -> Option B
  4. Quick Check:

    Extend Mock, implement interface = A [OK]
Hint: Mock classes extend Mock and implement the target interface [OK]
Common Mistakes:
  • Swapping extends and implements order
  • Using with instead of extends
  • Not implementing the interface
3. Given the following code snippet using mocktail, what will be the output of print(service.getData())?
class MockService extends Mock implements Service {}

final service = MockService();
when(() => service.getData()).thenReturn('Mocked Data');
print(service.getData());
medium
A. Returns 'Mocked Data'
B. Throws an error because getData is not defined
C. Returns null
D. Returns the real data from Service

Solution

  1. Step 1: Understand when-thenReturn usage

    The when() function sets up the mock to return 'Mocked Data' when getData() is called.
  2. Step 2: Check the print statement

    Calling service.getData() returns the mocked value 'Mocked Data'.
  3. Final Answer:

    Returns 'Mocked Data' -> Option A
  4. Quick Check:

    when().thenReturn() sets output = B [OK]
Hint: when().thenReturn() sets mock return value [OK]
Common Mistakes:
  • Expecting real method call instead of mock
  • Thinking it returns null by default
  • Confusing syntax causing runtime error
4. Identify the error in this mocktail test setup:
class MockService extends Mock implements Service {}

final service = MockService();
when(service.getData()).thenReturn('Data');
medium
A. Missing parentheses in when() argument
B. MockService should not implement Service
C. Incorrect use of when() without a lambda function
D. thenReturn() cannot return a string

Solution

  1. Step 1: Check when() syntax

    when() requires a lambda function like () => service.getData(), not a direct call.
  2. Step 2: Identify the error

    Passing service.getData() calls the method immediately, causing error.
  3. Final Answer:

    Incorrect use of when() without a lambda function -> Option C
  4. Quick Check:

    when() needs lambda = C [OK]
Hint: Use () => method() inside when() [OK]
Common Mistakes:
  • Calling method directly inside when()
  • Forgetting to implement interface
  • Misusing thenReturn() with wrong types
5. You want to test a Flutter widget that depends on a service fetching user data. How do mocks help in this scenario?
hard
A. Mocks allow you to simulate user data responses without real network calls
B. Mocks automatically generate UI for the widget
C. Mocks replace the widget with a simpler version
D. Mocks speed up the app by caching data

Solution

  1. Step 1: Understand widget dependency on service

    The widget depends on a service that fetches user data, usually from network.
  2. Step 2: Role of mocks in testing

    Mocks simulate the service to provide controlled user data without real network calls.
  3. Step 3: Benefits in testing

    This makes tests faster, reliable, and independent of external systems.
  4. Final Answer:

    Mocks allow you to simulate user data responses without real network calls -> Option A
  5. Quick Check:

    Mocks simulate data, no real calls = D [OK]
Hint: Mocks simulate service data to avoid real network calls [OK]
Common Mistakes:
  • Thinking mocks generate UI automatically
  • Confusing mocks with widget replacements
  • Believing mocks cache data for performance
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard