How To Write Unit Tests - Avoid Production Code in Tests

Using production code in test data setup is a bad idea because it makes tests depend on implementation details instead of the behavior they are meant to verify. When test data is built from the same code that the application uses, the tests become more likely to pass for the wrong reasons.

This article explores practical ways to keep your test data independent from production code so your tests stay focused on the behavior you want to verify.

Scenario

  • A use case that fetches an Order and formats the returned details
  • A unit test that verifies the use case
Use Case
class GetOrderUseCase {
    // ...
    async execute(params: { orderId: string }): Promise<GetOrderResponse> {
        const { orderId } = params;
        const order = await this.orderRepository.get(orderId);
        return {
            orderId: order.id,
            deliveryAddress: formatAddress(order.address),
        };
    }
}
Formatter
export const formatAddress = (address: Address): string => {
    const { street, houseNumber, city, zipCode } = address;
    return `${street} ${houseNumber}, ${city}, ${zipCode}`;
};
Unit Test
it("should get an order - BAD", async () => {
    // ...
    const address = Address.create({
        street: "Baker Street",
        houseNumber: "221B",
        city: "London",
        zipCode: "12345",
    });
    const order = Order.create({
        id: "1234567890",
        address,
    });
    const expected = {
        orderId: "1234567890",
        deliveryAddress: formatAddress(address),
    };
    //..

    const result = await useCase.execute({
        orderId: "1234567890",
    });

    expect(result).toEqual(expected);
});

The test coverage is 100%.

Uncaught Production Code Issues

The formatter code was changed. The test still passes, and the coverage is still 100%. The change was deployed to production.

Formatter
export const formatAddress = (address: Address): string => {
    const { street, houseNumber, city, zipCode } = address;
    return `${street} ${houseNumber}, ${city}, ${zipCode} DO NOT USE`;
};
Code Coverage

When referencing production code in test data setup, the test coverage can be misleading. Since the code is used in tests, the coverage tools will mark those lines as being hit, even though the tests are not actually verifying the behavior of that code. This can lead to a false sense of security, as developers may believe that their code is well-tested, when in reality, it is not.

Test Data Independent from Production Code

The following unit test will fail if production code changes.

Unit Test
it("should get an order - GOOD", async () => {
    // ...
    const address = Address.create({
        street: "Baker Street",
        houseNumber: "221B",
        city: "London",
        zipCode: "12345",
    });
    const order = Order.create({
        id: "1234567890",
        address,
    });
    const expected = {
        orderId: "1234567890",
        deliveryAddress: "Baker Street 221B, London, 12345",
    };

    // ...
    const result = await useCase.execute({
        orderId: "1234567890",
    });

    expect(result).toEqual(expected);
});
How to Avoid Test Data Coupling
  • Do not use production code to create test data
  • If setting up test data is difficult or verbose, consider adding methods to your code that will facilitate test data creation
  • Use constant, hard-coded values for test data
Testing "Helper" Methods

Quite often functionalities like formatters or mappers are implemented separately from the main business logic.

This also means they are easier to test in isolation, especially if they handle edge cases. That way the main logic stays clean.

Full code samples can be found on GitHub.

July 31, 2026