In the first part of this tutorial series, we looked at how to build scalable API with Dart and covered a few aspects:
- Setting up the development environment
- Creating your first API endpoint
- Building and organizing your API
- Integrating with database
In the second part, we'll look at how to test the API we have built and deploy it. We'll also highlight a few best practices.
Testing Your API
Testing your API is a critical step to ensure that it functions as expected and can handle different scenarios. In this section, we’ll explore how to test the API using both manual tools like Postman or HTTPie, as well as automated tests with Dart's built-in test
package.
Manual Testing with Postman
Follow the steps below to test your API endpoint with Postman:
Install Postman:
Download and install Postman from Postman’s official website.
Send a Request
- Open Postman and create a new request.
- Choose the method
GET
and enter the endpoint URL, e.g.,http://localhost:8080/user
. - Add the required headers, such as:
{
"Authorization": "Bearer test-token"
}
- Send the request and observe the response.
- Expected Response should look like this:
[{id: 1, name: Alice, email: alice@example.com, created_at: 2025-01-17 01:16:22.766942Z}, {id: 2, name: Bob, email: bob@example.com, created_at: 2025-01-17 01:16:22.766942Z}, {id: 3, name: Charlie, email: charlie@example.com, created_at: 2025-01-17 01:16:22.766942Z}]
GET
/user without Authorization Header:
Unauthorized
Unit Testing with the Test Package
Unit testing ensures that the API endpoints, middleware, and other functionality work as intended in isolation. In this section, we’ll write and organize tests to verify the behavior of our API.
Setting Up the Test Environment
Before writing tests, ensure the following setup:
- Add Test Dependencies: Add the
test
package to yourpubspec.yaml
:
dev_dependencies:
test: ^1.25.14
Run dart pub get
to install the package.
- Directory for Tests: Create a
test
directory in the project root:
my_api/
├── test/
│ ├── user_router_test.dart
│ ├── product_router_test.dart
│ └── auth_middleware_test.dart
Writing Unit Tests
Each unit test targets a specific part of the API, such as routes, middleware, or database interactions.
- Testing User Router
Filename: test/user_router_test.dart
import 'package:test/test.dart';
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart';
import 'package:my_api/routers/user_router.dart';
void main() {
group('User Router Tests', () {
final handler = Pipeline().addHandler(userRouter);
test('GET /user returns all users', () async {
final request = Request('GET', Uri.parse('/user'));
final response = await handler(request);
expect(response.statusCode, equals(200));
final body = await response.readAsString();
expect(body, contains('id')); // Assuming the response includes user IDs
});
test('GET /user with invalid path returns 404', () async {
final request = Request('GET', Uri.parse('/invalid'));
final response = await handler(request);
expect(response.statusCode, equals(404));
});
});
}
- Testing Authorization Middleware
Filename: test/auth_middleware_test.dart
import 'package:test/test.dart';
import 'package:shelf/shelf.dart';
import 'package:my_api/middleware/auth_middleware.dart';
void main() {
group('Auth Middleware Tests', () {
final handler = Pipeline()
.addMiddleware(authMiddleware())
.addHandler((Request request) => Response.ok('Authorized'));
test('Request with valid Authorization header passes', () async {
final request = Request(
'GET',
Uri.parse('/'),
headers: {'Authorization': 'Bearer test-token'},
);
final response = await handler(request);
expect(response.statusCode, equals(200));
expect(await response.readAsString(), equals('Authorized'));
});
test('Request without Authorization header fails', () async {
final request = Request('GET', Uri.parse('/'));
final response = await handler(request);
expect(response.statusCode, equals(403));
expect(await response.readAsString(), equals('Unauthorized'));
});
});
}
- Testing Database Access
You can mock the database or use a test database to test database-related functionality. Example file: test/user_dal_test.dart
import 'package:test/test.dart';
import 'package:mockito/mockito.dart';
import 'package:my_api/data/user_dal.dart';
class MockDatabaseConnection extends Mock implements UserDAL {}
void main() {
group('User DAL Tests', () {
final mockDb = MockDatabaseConnection();
test('getAllUsers returns a list of users', () async {
when(mockDb.getAllUsers()).thenAnswer((_) async => [
{'id': 1, 'name': 'John Doe', 'email': 'john.doe@example.com'}
]);
final users = await mockDb.getAllUsers();
expect(users.length, equals(1));
expect(users[0]['name'], equals('John Doe'));
});
test('getUserById returns a user if found', () async {
when(mockDb.getUserById(1)).thenAnswer((_) async => {
'id': 1,
'name': 'John Doe',
'email': 'john.doe@example.com'
});
final user = await mockDb.getUserById(1);
expect(user?['name'], equals('John Doe'));
});
});
}
Running the Tests
Run all tests using the following command:
dart test