Yes and no. It depends on what you are testing.
If the constant is part of a public API it should probably be tested. But
otherwise you're just making your tests more flaky.
Where it makes sense
If you have
class Currency {
public static readonly EUROS = 'EUR'
}
It might be acceptable to write a test like
test('Currency.EUROS is EUR', () => {
expect(Currency.EUROS).toBe('EUR');
})
In most cases this is overkill. But if your dealing with something where the
string of a currency matters a lot it might be make sense to write a test like this.
Where it doesn't make sense
It could be argued that you implicitly get the value of the test above by using
the constants directly inside other tests like this:
class CurrencyLocaleService {
getCurrencyForLocale(locale: string): string {
if (locale === Locale.SPAIN) {
return Currency.EUROS
}
...
}
}
...
describe('CurrencyLocaleService', () => {
const SPAIN_LOCALE = 'es_ES'
const CURRENCY_EUR = 'EUR'
test('getCurrencyForLocale with spain locale, returns euros', () => {
const sut = new CurrencyLocaleService();
expect(sut.getCurrencyForLocale(SPAIN_LOCALE)).toBe(CURRENCY_EUR);
})
})
This way we implicitly get the benefit of the first test. If the above test fails,
it will also mean that Currency.EUROS
has been changed.
What's more we are also implicitly checking the correctness of Locale.SPAIN
, so
if that ever changes this test will also fail.
In this way, the above test proves the correctness of the program a lot better than
this one:
describe('CurrencyLocaleService', () => {
test('getCurrencyForLocale with spain locale, returns euros', () => {
const sut = new CurrencyLocaleService();
expect(sut.getCurrencyForLocale(Locale.SPAIN)).toBe(Currency.EUROS);
})
})
However, I like this test better because it only tests the one thing. We might have
a legitimate reason to change the values of these constants, maybe some external
API needs currency to be in lower case 'eur'
. If that's the case then a test
that checks that getCurrencyForLocale with spain locale, returns euros
should not
fail, because while the string has changed Currency.EUROS
still means the same thing.
Maybe we will need to change our first test to this
test('Currency.EUROS is eur', () => {
expect(Currency.EUROS).toBe('eur');
})
But not both tests.
Tests aren't really about proving that your code is correct
I think the reason people like to write tests like this
describe('CurrencyLocaleService', () => {
const SPAIN_LOCALE = 'es_ES'
const CURRENCY_EUR = 'EUR'
test('getCurrencyForLocale with spain locale, returns euros', () => {
const sut = new CurrencyLocaleService();
expect(sut.getCurrencyForLocale(SPAIN_LOCALE)).toBe(CURRENCY_EUR);
})
})
Is the belief that the goal of our tests exist to show correctness and protect
the code from becoming incorrect. If someone comes along and changes the value
of Currency.EURO
all our tests will fail and there's no chance that the code
will make it to production.
However, we will need to change the code, and that doesn't mean that the code is now
wrong.
Tests aren't really about showing that your code is correct, I don't think. They're
about expressing what we expect our code to do. If I make a change to existing
code the tests should fail, but only to the extent that our expectations have changed.
If I change Locale.SPAIN
from 'es_ES'
to 'es-ES'
, this test should fail
test('Locale.SPAIN is "es_ES"', () => {
expect(Locale.SPAIN).toBe('es-ES');
})
if it exists, because the expectation changed. But no other test should fail.
Original article: https://www.ethancarlsson.dev/blog/constantsintests
Top comments (0)