Refactoring with Confidence
Make changes to your application without the fear of breaking it!
PROModule Outline
- Source Code & Resources PRO
- Lesson 1: Introduction PUBLIC
- Lesson 2: Introduction to Test Driven Development PUBLIC
- Lesson 3: Testing Concepts PUBLIC
- Lesson 4: Jest and Cypress PRO
- Lesson 5: A Simple Unit Test PRO
- Lesson 6: A Simple E2E Test PRO
- Lesson 7: Introduction to Angular's TestBed PRO
- Lesson 8: Setting up Tests with Jest and Cypress PUBLIC
- Lesson 9: Test Development Cycle PRO
- Lesson 10: Setting up Cypress & Jest in an Ionic/Angular Project PRO
- Lesson 11: The First Tests PRO
- Lesson 12: Injected Dependencies & Spying on Function Calls PRO
- Lesson 13: Building out Core Functionality PRO
- Lesson 14: Testing Asynchronous Code PRO
- Lesson 15: Creating a Mock Backend PRO
- Lesson 16: Setting up the Server PRO
- Lesson 17: Testing Integration with a Server PRO
- Lesson 18: Testing Storage and Reauthentication PRO
- Lesson 19: Refactoring with Confidence PRO
- Lesson 20: Conclusion PRO
Lesson Outline
Refactoring with Confidence
Our application isn't the most attractive looking piece of software in the world - it's completely bare bones with no styling at all - but it does its job. Given that we have strictly adhered to the Test Driven Development methodology, we can be reasonably confident that it does the tasks that it is supposed to do: the requirements that we built into it via the test we wrote.
Having all of these tests is a great investment for the future, as they will always be sitting there protecting against regressions when any changes are made in the future. This means that we can confidently refactor the application, or add new features, without having to worry too much about breaking existing functionality.
Perhaps we have shelved this application for a while, or maybe we are coming on as a new developer to this project, and we see the following code in src/app/home/home.page.ts
:
ngOnInit(){
this.modules = [
{id: 1, title: 'Module One', description: 'Test'},
{id: 2, title: 'Module Two', description: 'Test'},
{id: 3, title: 'Module Three', description: 'Test'},
{id: 4, title: 'Module Four', description: 'Test'},
{id: 5, title: 'Module Five', description: 'Test'}
];
}
The modules array is being manually defined in the ngOnInit
function, and our implementation of the actual modules service isn't even complete at this point (it is still using dummy data). Let's say we decide it's time to change this. We know that the application is well tested, so we don't need to worry too much about breaking something inadvertently with this refactor - if we do unintentionally break something that was previously working then the tests are likely going to notify us.
Let's give that a go. We will refactor this code to load in its data from the ModuleService
instead. Usually we start with an E2E test whenever we are introducing some new functionality, but in this case we already have our tests in place - we are just changing the implementation. Sometimes this will mean having to update the unit tests, but since we are using more "behavioural" style tests, e.g:
the modules class member should contain 5 modules after ngOnInit has been triggered
the way in which we set up the modules
array doesn't really matter, just as long as after ngOnInit
is triggered it is there. Given that these tests are in place and we don't need to modify them, we are going to jump straight into pulling data from our ModuleService
.
We are still just going to hard code the data into the service, but we could easily change it to instead load from some external source as well. Let's add a simple test to our spec file to cover this new functionality.
Modify
src/services/module.service.spec.ts
to reflect the following:
import { TestBed } from '@angular/core/testing';
import { ModulesService } from './modules.service';
describe('ModulesService', () => {
let service: ModulesService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(ModulesService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
it('getModules should return a non-empty array', () => {
const result = service.getModules();
expect(result.length).toBeGreaterThan(0);
});
});
We have just added a single test that checks to see if getModules
returns a non empty array. This is a more granular unit test, it's not behavioural like the previous test we discussed. In this case, if we were to change this in the future to load the data in from a server, for example, then the test would need to be updated because it would no longer return an array (it would likely return an observable instead).
Let's add our dummy implementation:
getModules() {
return [];
}
If we run the unit tests now with npm test
we should get the following error:
FAIL src/app/services/modules.service.spec.ts
● ModulesService - getModules should return a non-empty array
expect(received).toBeGreaterThan(expected)
Expected: > 0
Received: 0
Thanks for checking out the preview of this lesson!
You do not have the appropriate membership to view the full lesson. If you would like full access to this module you can view membership options (or log in if you are already have an appropriate membership).