1

I have a function that looks roughly like this:

async function getValue(connection: Connection): Promise<number> {
    const value = await connection.getValue();
    return value < 0 ? value : 0;
}

The important thing to note about this is that Connection is some huge megaclass and mocking it would take a significant amount of work. I have a function I'm trying to write tests for that calls getValue two or three nested function calls down, but am having difficulty writing said tests due to Connection not being available in my unit testing environment. The first way of approaching this I thougt of would be to create a new class, something like ValueGetter that abstracts away Connection and then I could just pass in a mock of that into my top level function, like below

class ValueGetter{
    connection : Connection;

    constructor(connection: Connection){
        this.connection = connection;
    }

    async getValue(): Promise<number> {
        const value = await this.connection.getValue();
        return value;
    }
}

class MockValueGetter{
    async getValue(): Promise<number> {
        return 0;
    }
}

This seems a bit hacky though, so I was wondering if anyone here had a better approach. Thanks a lot!

6
  • "Connection is some huge megaclass" I think you know what your real problem is here. I don't particularly like your proposed solution but if you don't have the time to fix the root cause, sometimes you have to do ugly things. Commented Jul 5, 2023 at 9:58
  • Yes, I'm aware that the real problem is that. But like you correctly recognized, i unfortunately don't have the time at the moment to fix that & it stems from an external library, so I'm looking for alternative approaches
    – Ahri
    Commented Jul 5, 2023 at 10:02
  • 1
    I don't follow why it's hard to mock Connection - don't you just need to provide an object with a function that returns a promise of a number?
    – bdsl
    Commented Jul 5, 2023 at 10:38
  • This is due to my code example I'm afraid - in reality the return type is a bit more complex than number and getting it is also a bit more involved than just calling .getValue(). I didn't add the full code to keep things simpler and focus on the problem at hand
    – Ahri
    Commented Jul 5, 2023 at 10:53
  • If the complexity and purpose of the function is getting a simple value out of a complex connection then I think you are going to need to mock the, or use a real, connection object. Otherwise any way you organise it you've got a gap in your tests.
    – Ewan
    Commented Jul 5, 2023 at 11:31

1 Answer 1

4

Try giving it a functional core.

Put the interesting behavior logic in there. The rest is structural code that isn't really interesting and doesn't need unit testing nearly as badly.

function nonPositive(value: number): number {
    return value < 0 ? value : 0;
}

This is easy to unit test. And it was part of the code until it got forgotten. This is the behavior code. The rest is just reaching out to find stuff. The less your unit tests even know about where stuff is the better. When tests know where stuff is it becomes harder to move it. Write tests that make refactoring easy. Not harder.

Not the answer you're looking for? Browse other questions tagged or ask your own question.