Vuetify 3 TypeScript Tutorial Series -Part 11
In Part 10 of this tutorial series, we covered the following steps:
- Wrote tests for
taskApi.ts
- Wrote tests for
editTask.ts
- Wrote tests for
generateTask.ts
If you missed Part 10, you can find it here: Part 10
Test fetch tasks
Okay let us start to write a test for the composable file getTasks.ts
so create in tests
-> impl
the file getTasks.test.ts
and add these lines:
import {describe, expect, it, vi} from 'vitest';
import {taskService} from "../../src/services/taskApi";
import {getTasks} from "../../src/composables/getTasks";
import {AxiosError} from "axios";
import {mockTaskFetchResponse} from "../helper/mockResponse";
describe('getTasks tests', () => {
it('fetchTasks fills tasks array with mockTasksResponse', async () => {
// Mock taskService.getTasks to return mockTasksResponse
taskService.getTasks = async () => ({data: mockTaskFetchResponse});
// Call fetchTasks
const {fetchTasks, tasks, isLoading, isNetworkError} = getTasks();
await fetchTasks('');
// Assert tasks array contains mockTasksResponse
expect(tasks).toEqual(mockTaskFetchResponse);
expect(isLoading.value).toBe(false);
expect(isNetworkError.value).toBe(false);
});
it('handles error in fetchTasks', async () => {
const errorMessage = 'Network error';
const mockError = new AxiosError(errorMessage);
// Replace getTasks with a function that rejects with the mock error
taskService.getTasks = vi.fn(() => Promise.reject(mockError));
// Use the composable function
const {fetchTasks, isLoading, isNetworkError, axiosError} = getTasks();
// Call fetchTasks and expect it to handle the error
await fetchTasks('');
// Check the states of isLoading, isNetworkError, and axiosError
expect(isLoading.value).toBe(false);
expect(isNetworkError.value).toBe(true);
expect(axiosError.value).toEqual(mockError);
expect(mockError.message).toEqual(errorMessage);
});
});
Testing Successful Task Fetching:
- The first test checks if
fetchTasks
successfully fills thetasks
array withmockTaskFetchResponse
. - It mocks
taskService.getTasks
to return a resolved promise withmockTaskFetchResponse
. - Upon calling
fetchTasks
, it verifies that thetasks
array matches the mock response,isLoading
is false (indicating loading has completed), andisNetworkError
is false (no network error occurred).
Testing Error Handling in Task Fetching:
- The second test simulates an error during task fetching.
- It mocks
taskService.getTasks
to return a rejected promise withmockError
(anAxiosError
instance containing a network error message). - After calling
fetchTasks
, it checks the application's state: isLoading
should be false (loading process ended).isNetworkError
should be true (an error occurred).
Another way to run all tests is to enter in Terminal:
pnpm test:unit
You should see this output:
Test remove task
Next, we should write a test for the composable file removeTask.ts
so create in tests
-> impl
the file removeTask.test.ts
and add these lines:
import {describe, expect, it, vi} from 'vitest';
import {taskService} from "../../src/services/taskApi";
import {removeTask} from "../../src/composables/removeTask";
import {Ref, ref} from "vue";
import {AxiosError} from "axios";
describe('removeTask tests', () => {
const id = 1;
const isLoading: Ref<boolean> = ref(false);
const isNetworkError: Ref<boolean> = ref(false);
const axiosError: Ref<AxiosError | unknown> = ref(null);
const fetchTasks = vi.fn();
const taskType = 'completed';
it('when delete task is called then expect success path', async () => {
taskService.deleteTask = async () => ({});
await removeTask(id, isLoading, isNetworkError, axiosError, fetchTasks, taskType);
expect(isLoading.value).toBe(false);
expect(isNetworkError.value).toBe(false);
expect(fetchTasks).toHaveBeenCalled();
});
it('when delete task is called then expect network error', async () => {
const errorMessage = 'Network error';
const mockError = new AxiosError(errorMessage);
taskService.deleteTask = vi.fn(() => Promise.reject(mockError));
await removeTask(id, isLoading, isNetworkError, axiosError, fetchTasks, taskType);
expect(isLoading.value).toBe(false);
expect(isNetworkError.value).toBe(true);
expect(axiosError.value).toEqual(mockError);
expect(mockError.message).toEqual(errorMessage);
});
});
Testing Successful Task Deletion:
- The first test, labeled “when delete task is called then expect success path,” simulates a successful task deletion.
- It mocks
taskService.deleteTask
to return an empty response, indicating success. - After calling
removeTask
, it checks: isLoading
is false, showing the loading state has ended.isNetworkError
is false, indicating no error occurred.
Testing Error Handling in Task Deletion:
- The second test, “when delete task is called then expect network error,” simulates an error scenario during task deletion.
- It mocks
taskService.deleteTask
to return a rejected promise withmockError
(anAxiosError
instance).
After calling removeTask
, it verifies:
isLoading
is false, confirming the end of the loading state.isNetworkError
is true, indicating an error was encountered.axiosError
matchesmockError
, ensuring the error is correctly captured and stored.- The error message in
mockError
is as expected (errorMessage
).
Now, let us see the test coverage for our tests, enter in the Terminal:
pnpm test:coverage
You should see this in the Terminal:
There is also a folder coverage
in your project with a file index.html
:
Command shortcuts
Okay, after we have now finished the testing part for our app. Let us take a look at our command shortcuts in package.json
:
Summary of the shortcut commands:
"dev": "vite"
: Starts the Vite development server. Runningpnpm dev
will start your project in development mode, usually with hot module replacement (HMR), making it easy to develop and test changes in real-time."build": "vite build"
: Compiles and bundles your application for production deployment. Runningpnpm build
will generate the production files, typically in adist
directory."preview": "vite preview"
: Serves the production build of your app for testing. This command lets you locally preview the production build after running the build script."lint": "eslint . --fix --ignore-path .gitignore"
: Runs ESLint to analyze your code for potential errors or code style issues and automatically fixes them where possible. The--ignore-path .gitignore
option ensures it skips files ignored by Git."test:unit": "vitest"
: Runs unit tests using Vitest, a test runner. This script is used to execute the test cases written in your project."test:coverage": "vitest --coverage"
: Generates a test coverage report using Vitest. This will tell you how much of your codebase is covered by tests, which is useful for maintaining your project.
The keys in the scripts
section of your package.json
from your Vuetify project define various command shortcuts that can be run from the terminal to perform common tasks in the development process.
Since we are already familiar with pnpm dev
& pnpm test:unit
& pnpm test:coverage
. Let us run now pnpm build
in the Terminal:
You should see the following outcome:
As you can see the build was successful and you now have a dist
folder:
You must test always if pnpm build
is successful because if it fails there is something wrong with your project and you need to fix it. Because no successful build also means you can’t create a Docker image out of your project (What we will do in the next part).
The next shortcut command to test is pnpm preview
:
By clicking on http://localhost:4173/ you should see the production build:
Lastly let us see if our ES Linter works by entering pnpm lint
in the Terminal:
Congrats / Mabrook / مبروك no linter warnings in the project.
With that, we conclude the first part of this tutorial series. If you found it useful and informative, give it a clap. Here is the final and last part, Part 12
Don’t forget to check out the video playlist on YouTube.
Here is the source code on GitHub, check out the branch: part-eleven