Hey Coders!!!
Let's continue with the last part of my project on NestJS using TDD approach.
Remember the core concepts!
Write a Failing Test:
The first step in the TDD cycle is to write a test for a small piece of functionality that you want to implementWrite Minimal Code to Pass the Test:
The next step is to write the minimal amount of code necessary to make the test passRefactor the Code:
Once the test is passing, the final step is to refactor the code to improve its structure and readability.
This cycle, often referred to as "Red-Green-Refactor," ensures that you always have a working version of your code and that all parts of your codebase are covered by tests.
We have to add the removed functionality, so let's write our tests for the service in
src/properties/test/properties.service.spec.ts
// remove
describe('remove', () => {
it('should remove a property', async () => {
const mockProperty = {
id: 1,
name: 'Test Property',
description: 'Test Description',
location: 'Test Location',
imageUrl: 'http://example.com/image.jpg',
unitsAvailable: 5,
hasWifi: true,
hasLaundry: false,
};
// Mockeamos la búsqueda de la propiedad
jest.spyOn(repository, 'findOne').mockResolvedValue(mockProperty);
// Mockeamos la eliminación
jest.spyOn(repository, 'remove').mockResolvedValue(mockProperty);
const result = await service.remove(1);
expect(result).toEqual(mockProperty);
expect(repository.findOne).toHaveBeenCalledWith({ where: { id: 1 } });
expect(repository.remove).toHaveBeenCalledWith(mockProperty);
});
it('should throw NotFoundException if property does not exist', async () => {
// Mockeamos que la propiedad no se encuentra
jest.spyOn(repository, 'findOne').mockResolvedValue(null);
await expect(service.remove(999)).rejects.toThrow(NotFoundException);
expect(repository.findOne).toHaveBeenCalledWith({ where: { id: 999 } });
expect(repository.remove).not.toHaveBeenCalled();
});
});
Using the Tdd approach, run the test with
npm run test
We will check some issues, we are missing the function, that is the next part will be adding in properties.service.ts
async remove(id: number): Promise<Property> {
const propertyToRemove = await this.propertiesRepository.findOne({ where: { id } });
if (!propertyToRemove) {
throw new NotFoundException(`Property with ID ${id} not found.`);
}
await this.propertiesRepository.remove(propertyToRemove);
return propertyToRemove;
}
After this we run the tests again with npm run test.
We will have one issue, we are missing the remove part in the mockRepository on the beforeEach part.
beforeEach(async () => {
const mockRepository = {
find: jest.fn().mockResolvedValue([]),
findOne: jest.fn().mockImplementation((options) =>
options.where.id === 999
? Promise.resolve(null)
: Promise.resolve({
id: 1,
name: 'Test Property',
description: 'Test Description',
location: 'Test Location',
imageUrl: 'http://example.com/image.jpg',
unitsAvailable: 5,
hasWifi: true,
hasLaundry: false,
}),
),
create: jest.fn().mockImplementation((dto) => dto),
save: jest
.fn()
.mockImplementation((property) =>
Promise.resolve({ id: 1, ...property }),
),
remove: jest.fn().mockImplementation((property) =>
Promise.resolve(property),
),
};
const module: TestingModule = await Test.createTestingModule({
providers: [
PropertiesService,
{
provide: getRepositoryToken(Property),
useValue: mockRepository,
},
],
}).compile();
service = module.get<PropertiesService>(PropertiesService);
repository = module.get<Repository<Property>>(getRepositoryToken(Property));
});
Then run the tests
npm run test
Wuhuuuu!!! we have only to add the controller part.
Let's work on this, the next part is creating the tests for the controller
In src/properties/test/properties.controller.spec.ts
We will add this code
describe('remove', () => {
it('should remove a property', async () => {
const mockProperty = {
id: 1,
name: 'Test Property',
description: 'Test Description',
location: 'Test Location',
imageUrl: 'http://example.com/image.jpg',
unitsAvailable: 5,
hasWifi: true,
hasLaundry: false,
};
// Mockeamos la función remove del servicio
(service.remove as jest.Mock).mockResolvedValue(mockProperty);
const result = await controller.remove(1);
expect(result).toEqual(mockProperty);
expect(service.remove).toHaveBeenCalledWith(1);
});
});
We learned from the previous function, so let's add the part in the mockRepository in the same file
remove: jest.fn().mockImplementation((id: number) =>
Promise.resolve({ id }),
),
We know if we run the testing, it will fail.
npm run test
We are missing the remove function in
src/properties/properties.controller.ts
@Delete(':id')
async remove(@Param('id') id: number): Promise<Property> {
return this.propertiesService.remove(id);
}
And then after running npm run test
All test are passed
Mmmmmm
We are missing our last part, adding swagger to see our project like this:
that is one task for you...
I will share the final version on my github of the project and also the third part with each step of this tutorial.
I enjoyed a lot to do this tutorial, the TDD approach is a way of programming and it has a lot of benefits (also some cons), but it is one of the best way to have your project always ready to run with no problems!!!
See you Coders, if this post is useful for you, please like and share it! :)
Github Last Part:
https://github.com/joeaspiazudeveloper/nestjs-houselocation-tdd/tree/final-part-removeRest
Github Completed CRUD properties with Swagger
https://github.com/joeaspiazudeveloper/properties-api-nestjs