Code Testing
Unit Testing
Overview
Unit testing is the practice of testing individual parts (units) of code independently to ensure they function correctly on their own.
Purpose of Unit Testing
Early Bug Identification: Identify and fix issues during development, preventing them from becoming bigger problems later.
Why Is It Necessary?
- Early Error Detection: Find and fix errors before they integrate into larger parts of the code.
- Time and Effort Saving: Solve problems early on to save time and effort down the line.
- Maintain Code Quality: Ensure stability and reliability in the codebase.
Benefits of Unit Testing
- Reduced Debugging Time: Less troubleshooting is needed in later stages.
- Improved Code Reliability: Build robust and dependable code.
- Simplified Updates: Make it smoother to implement changes.
Writing Test Cases
Step 1: Preparation
- Create Unit Test Cases and mock Files in the Project
- Create test and mock files within the test folder of the project, following the guidelines shown below.
- The file name should be written in snake_case. The general test file naming convention should be
file_name_test.dart.- File Naming Convention:
- Feature File Name: trolley.dart
- Test File Name: trolley_test.dart
- Mock Data File Name: trolley_mock_data.dart
- File Naming Convention:
- Suppose you’re writing test cases for the trolley.dart then the path where you should create test cases and mock files is shown below:
- File for which Test Cases Need to be Written:
- File Path: lib/backend/api/services/trolley/model/trolley.dart
- Test & Mock File Paths:
- Test File Path: test/backend/api/services/trolley/model/trolley_test.dart
- Mock File Path: test/backend/api/services/trolley/model/mock.dart
- File for which Test Cases Need to be Written:
Step 2: Mock Data
- For unit test cases, you have to create mock data files containing necessary mock classes and functions which you can use within your test cases to validate different code blocks.
- Start the names of your mock data variable with the word "mock" and use camelCase.
- Make sure all the mock data for related things are close to each other. This makes it easier to find and understand them in your code.
- Leave one empty line between each mock data. This helps keep your code clean and easy to read.
const mockProduct = Product(
code: 'Product Code',
name: 'Product Name',
slug: 'Product Slug',
description: 'Product Description',
prices: mockProductPrices,
image: 'Product Image URL',
brand: 'Product Brand',
packSize: 'Product Size',
packQuantity: 'Product Quantity',
);
Step 3: Test Case Logic
- In the group description, you should only write the name of the class, service, repository, or bloc.
- In the test cases description, you should only write the method's name or property with a dot at the beginning, like .fromJson.
group("Name of class/ service/ repo/ bloc:", () { test(".method name/ property", () { ... }); }); - If you have to test one method in multiple test functions for different use cases club them in another group like the following:
group("Name of class/ service/ repo/ bloc:", () { group(".method name/ property", () { test("completes without any errors", () { ... }); test("handles exception gracefully", () { ... }); }); }); - In the test code, always place the actual result first, followed by the expected result (matcher).
group("Name of class/ service/ repo/ bloc:", () { test(".method name/ property", () { Actual Result, // Actual Result comes first, then the Matcher Result Matcher Result }); test('.fromJson', () { expect( PriceTotal.fromJson(mockPriceTotalJson), // Actual Result mockPriceTotal, // Matcher Result ); }); });
So the final test cases file for trolley_test.dart looked like something which was shown below:
import 'package:flutter_test/flutter_test.dart';
import 'package:toolstation/backend/backend.dart';
import 'mock.dart';
void main() {
group('PriceTotal Model', () {
test('.fromJson', () {
expect(
PriceTotal.fromJson(mockPriceTotalJson),
mockPriceTotal,
);
});
test('.toJson', () {
expect(
mockPriceTotal.toJson(),
mockPriceTotalJson,
);
});
});
group('Price Model', () {
test('.fromJson', () {
expect(
Price.fromJson(mockPriceJson),
mockPrice,
);
});
test('.toJson', () {
expect(
mockPrice.toJson(),
mockPriceJson,
);
});
});
group('TrolleyTotalsResponse Model', () {
test('.fromJson', () {
expect(
TrolleyTotalsResponse.fromJson(mockTrolleyTotalsResponseJson),
mockTrolleyTotalsResponse,
);
});
test('.toJson', () {
expect(
mockTrolleyTotalsResponse.toJson(),
mockTrolleyTotalsResponseJson,
);
});
test('.copyWith', () {
expect(
mockTrolleyTotalsResponse.copyWith(),
mockTrolleyTotalsResponse,
);
expect(
mockTrolleyTotalsResponse.copyWith.data(mockTrolleyTotalsResponse.data),
mockTrolleyTotalsResponse,
);
});
});
}