Should you create new constants for tests

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.