Now in the setup method, we need to create a object of the MockQuotesService class and pass that object to the QuotesNotifier constructor.
setUp(() {
mockQuotesService = MockQuotesService();
sut_quotesNotifier = QuotesNotifier(mockQuotesService);
});
Let’s start by writing the test to test if the default values of QuotesNotifier are set correctly. So in the future, if anyone changes any value, the test will update us.
The default values of QuotesNotifier should be isLoading should be false, quotes should be an empty list.
Here is a Challenge for you. Pause the video and try to write the test.
I hope you completed the challenge. If not, I suggest you watch the video on writing the first unit test.
test('Check initial values are correct', () {
// assert
expect(sut_quotesNotifier.isLoading, false);
expect(sut_quotesNotifier.quotes, []);
});
Let’s run this test and check if it passes. You can also change the values and check if the test fails to make sure that the test is working.
There are multiple functions that we need to test in the QuotesNotifier class. We can create a group of tests and by executing that group, all the tests will run inside the group.
group('get quotes', () {
// test cases goes here
});
Now we will test if the getQuotes function returns the quotes from QuotesServices.
test('getQuotes should call getQuotes function', () async {
// Arrange
when(() => mockQuotesService.getQuotes()).thenAnswer((_) async => []);
// Act
await sut_quotesNotifier.getQuotes();
// Assert
verify(() => mockQuotesService.getQuotes()).called(1);
});
The above test is following the AAA pattern.
Arrange - In the Arrange section, we are setting up the mockQuotesService to return an empty list when the getQuotes function is called.
Act - In the Act section, we are calling the getQuotes function.
Assert - In the Assert section, we are verifying if the getQuotes function is called once.
The test will fail if the getQuotes function is not set. But we have already done that part for you. So you don’t have to worry about it. Just run the test, and you can see the test passing. You can comment on the code inside the getQuotes function and run the test to see if the test fails.
Now let’s do the next test: to test if the QuotesService is called or not. We must also ensure the fields are updated correctly, and the data is populated.
Write the following test inside the get quotes group.
test('''Loading data indicator,
sets quotes, indicates data is not loaded anymore''', () async {
// arrange
when(() => mockQuotesService.getQuotes()).thenAnswer((_) async => [
Quotes( 1,'Test Quote 1','Test Author 1'),
Quotes(2, 'Test Quote 2','Test Author 2'),
Quotes(3,'Test Quote 3','Test Author 3',)
]);
final future = sut_quotesNotifier.getQuotes();
expect(sut_quotesNotifier.isLoading, true);
await future;
expect(sut_quotesNotifier.quotes, [
Quotes( 1,'Test Quote 1','Test Author 1'),
Quotes(2, 'Test Quote 2','Test Author 2'),
Quotes(3,'Test Quote 3','Test Author 3',)
]);
expect(sut_quotesNotifier.isLoading, false);
});
The above test first sets up the mockQuotesService to return a list of quotes. Then it calls the getQuotes function and stores the future in a variable. Then it checks if the isLoading is true. Then it waits for the future to complete and checks if the quotes are populated correctly and the isLoading is false.
It’s good to follow the DRY (Do not repeat yourself) principle. So we refactor the code in such a way that it can be reused and we can avoid code duplication.
First, we cut the List of dummy Quotes and paste it into the constant file like this.
final mockQuotesForTesting = [
Quotes(
1,
'Test Quote 1',
'Test Author 1',
),
Quotes(
2,
'Test Quote 2',
'Test Author 2',
),
Quotes(
3,
'Test Quote 3',
'Test Author 3',
),
];
Now use mockQuotesForTesting instead of the list of Quotes.
We will also extract the Arrange part of the AAA test code into a function so that we can reuse it and paste it on top inside the group test function.
void arrageQuotesServiceReturnsQuotes() {
when(() => mockQuotesService.getQuotes())
.thenAnswer((_) async => mockQuotesForTesting);
}
We will use arrageQuotesServiceReturnsQuotes where ever we need to set up the mockQuotesService to return the list of quotes.
Finally, this is what our quotes ChangeNotifier test looks like.
class MockQuotesService extends Mock implements QuotesService {}
void main() {
late QuotesNotifier sut_quotesNotifier;
late MockQuotesService mockQuotesService;
/// Initialise or set up everything needed for the test.
/// runs everytime before each and every test
setUp(() {
mockQuotesService = MockQuotesService();
sut_quotesNotifier = QuotesNotifier(mockQuotesService);
});
test('Should check initial values are correct', () {
expect(sut_quotesNotifier.isLoading, false);
expect(sut_quotesNotifier.quotes, []);
});
group('getQuotes', () {
void arrageQuotesServiceReturnsQuotes() {
when(() => mockQuotesService.getQuotes())
.thenAnswer((_) async => mockQuotesForTesting);
}
test('Get Quotes using the QuotesService', () async {
arrageQuotesServiceReturnsQuotes();
await sut_quotesNotifier.getQuotes();
verify(() => mockQuotesService.getQuotes()).called(1);
});
test('''Loading data indicator,
sets quotes, indicates data is not loaded anymore''', () async {
arrageQuotesServiceReturnsQuotes();
final future = sut_quotesNotifier.getQuotes();
expect(sut_quotesNotifier.isLoading, true);
await future;
expect(sut_quotesNotifier.quotes, mockQuotesForTesting);
expect(sut_quotesNotifier.isLoading, false);
});
});
}
Now let’s run the main method and check if all the tests pass.