This time last year, I didn’t even know what unit tests were. I just wrote code, and as long as they worked the way they were supposed to, it would be pushed to production.
Then I joined a larger tech company and the codebases and processes were a different ball game altogether, and I had to quickly get accustomed to them. This article will contain some random things I’ve learnt along the way while learning how to write unit tests.
1. The goal of unit tests
This one may seem obvious, but it helped me understand unit tests a lot better:
- Ensure that methods get called when they are supposed to
- Ensure that values definitions are as expected
2. When possible, it’s good to test the values each method was called with
This will make tests more accurate. For example, something like:
expect(mockMethod.findElm).toHaveBeenCalledWith(mockArray);
would be better instead of
expect(mockMethod.findElm).toHaveBeenCalled();
3. Values set when a class is instantiated should be spied on before the class is injected in the test
Spying on values that are set during instantiation, after the class has been instantiated will not work. For example, given a class like this:
export class mockClass {
constructor(){
this.mockValue = this.mockMethod.findElm(); //gets index
}
}
To test the value of mockValue
, you will need to do something like
spyOn(mockMethod.findElm).and.returnValue(1);
component = TestBed.inject(mockClass);
expect(component.mockValue).toEqual(1);
Doing something as below will not work. This is because when mockClass
is injected, mockMethod.findElm
is already called, and at that point in time, mockValue
is undefined. Returning a mock value after the method has already been called will not override values that are already set.
component = TestBed.inject(mockClass);
spyOn(mockMethod.findElm).and.returnValue(1);
expect(component.mockValue).toEqual(1);
4. It’s not necessary to provide all required parameters in mock variables if they’re not being used.
This will make your mock variables more focused, and also makes it faster to write tests. The below example is demonstrated using Typescript
interface IMockInterface {
errorCode: HttpStatusEnum,
message: string
}const mockVariable: IMockInterface = {
message: 'mock string',
} as IMockInterface;
As seen above, if our test only requires mockVariable.message
, then we do not need to include errorCode
even though it’s required by the interface. We can simply add as IMockInterface
at the back. When I first started writing unit tests, I kept feeling the need to include everything, but now it’s just so much easier not to.
5. It’s good to mock variables to follow their expected format
This one is more of a preference really. I find that it helps improve code readability as it allows other developers to quickly understand what the expected format should look like.
For example, a mock ISO date string could have a mock value of '2022-08-04T17:50:00+0000'
instead of 'mock date string'
.
So yeah, those are some of the things I’ve learnt when I was getting started with unit tests. Some of them may be pretty obvious, but I hope it can help someone who is in the same boat I was in!