Fundamentals of Unit Testing in Software Development
Automated testing is the practice of writing code that is separate from the software being tested. The main purpose of automated testing is to find broken parts of your application before it reaches production.
- The good thing about automated tests is they are reusable. You write them once and run them a million times.
- Lots of teams find it beneficial to run their tests with every commit. With this approach, it makes sure breaking changes are caught way before a pull request is accepted into the master branch.
- Writing automated tests also helps you write better code. When writing tests, you’re forced to think about the edge cases of every function in your application. You write tests for these functions, giving them various inputs and make sure they behave as you expect.
Why write Automated Tests
The good thing about automated tests is they are reusable. You write them once and run them a million times.
- Gets rid of the fear of refactoring our code.
- Reduces the required amount of manual testing.
- Provides fast feedback about the code’s behavior.
- Bugs can be identified in development phase, instead of creeping in Production.
- Inherently increases code quality by forcing developers to put more focus on their code
Types of Tests
1. Unit Testing
2. Integration Testing
3. End To End Tests
Test a unit of application without any external dependencies like files, databases, queues, web services etc.)
- Cheaper to write
- Executes fast
- Don’t dove a lot of confidence wrt overall system.
Tests the application with its external dependencies. You test the integration of code with the external systems.
- Takes longer to execute
- Gives more confidence
End To End Testing
Drive an application through its UI.
- Gives you greatest confidence
- Very slow
- Very brittle
Unit Tests features
- Unit tests are First-class citizens. They are as important as the production code. It means all the best practices about writing clean, and maintainable code does apply to test methods as well.
- Each test should have a single responsibility. Large, fat and messy test methods that are really unmaintainable. When these tests break, they spend so much time on debugging. So as you are writing tests, you need to keep them clean.
- Unit Tests should not have any logic. It should not contain any loops, conditionals.
- Each test should be Isolated. It means that each should behave as if its the only test in the world.
- Unit test should not be too specific, and not too general.
What to test
Always test the outcome of a method.
A function can be-
- Function which returns a value. Write a test to check if the function is returning the right value.
- Function that performs an action. This can include changing the state of an object in memory, database.
- These function might return a value as well. Write a test to check the state outcome that is the given object in memory is in right state.
- If the outcome is to talk to an external resource like a web service or database, then you should verify that the class under test is making the right call to these external dependency.
What not to test
- Do not test any language features.
- Do not test any third party code or library.
Arranging your tests
Arrange, Act, Assert is a common pattern when unit testing.
- Arrange your objects, creating and setting them up as necessary.
- Act on an object.
- Assert that something is as expected.
Normally, a class or a function in Production code is not isolated, and often talks to external objects like web services, APIs, Database, File Store.
To unit test such classes and function, we need to mock the behavior of external object so that feature is tested standalone and independently.
Mocking is a process to imitate the behavior of an external object. Mocking enables us to test the parts of a system in isolation. In simple words, mocking is creating objects that simulate the behavior of real objects.
Why use Mock Objects
Mocking are recommended in few conditions-
1) Improved test execution speed.
- Slow algorithms
- External resource- DB, Web Service etc.
2) Support parallel development streams
- If real object still in development stage.
- Can mock the object and continue development.
3) Improves Test reliability
4) Reduce development/testing costs
- If external company bills per usage, then mocking that external object will save cost.
TDD (Test Driven Development)
An approach where we write the tests before writing the production code.
How TDD Works
- Start by writing a failing test.
- Write the simplest code to make the test pass.
- Refactor your code if necessary.
Repeat the above steps over and over until a complete feature is built.
Benefits of TDD
- Source code will testable right from the start.
- Full Code Coverage by Tests.
- Often results in a simpler implementation.
Code First Or Test First
In TDD, we write tests before we code, so our development is driven by Tests. In Code first, we write code first and then write tests, which we do it normally.
In theory, TDD is more promising because of the benefit mentioned above. But in Practice, sometimes it can really get complex and might slow you down. If that’s the case, it’s better to switch to code first approach and write our tests later.
- Its recommended to write tests for your code.
- These tests gives you confidence on your code.
- The tests should be readable, maintainable and clean. Follow all the best practices in your tests that you usually follow while writing code.
- Mocking is recommended if you are interacting with an external object.
- TDD is one of the recommended practices while developing an application. It helps us validating each functionality even before implementing it.
This article is based on the below course on Udemy.