Node.js

How to access and test an internal non-exports function in a nodejs module

25 September 2026 · 9 min read

How to access and test an internal non-exports function in a nodejs module

Testing is a cornerstone of robust software development, and Node.js modules are no exception. While externally exposed functions are straightforward to test, the challenge arises when you need to access and test an internal (non-exports) function in a Node.js module. These internal functions, often crucial for the module’s logic, are deliberately hidden from the outside world. Understanding how to effectively test these functions is vital for ensuring code quality and preventing unexpected behavior. This article delves into various strategies and techniques to help you navigate this common testing scenario, providing you with the knowledge to write comprehensive and reliable tests for your Node.js modules. We’ll explore different approaches, considering their pros and cons, and equip you with the tools to make informed decisions about your testing strategy. We will also review the significance of testing internal functions, especially in regards to code maintenance and refactoring.

Understanding the Need for Testing Internal Functions

Why bother testing functions that aren’t meant to be directly accessed? The answer lies in the complexity and interdependence of code. Internal functions often perform critical tasks within a module, and their failure can cascade into visible issues with the exported functions. By directly testing these internal components, you gain a deeper understanding of the module’s inner workings and can identify potential problems before they surface in production. This is especially important when refactoring or making changes to the module’s internal logic. A study by the Consortium for Information & Software Quality (CISQ) found that well-tested code has significantly fewer defects and is easier to maintain (CISQ). Testing internal functions significantly contributes to this goal.

Consider a module that handles complex data validation. While the exported function might simply be validateData(data), it likely relies on several internal functions to perform specific validation checks, such as _checkDataType(value), _checkRequiredFields(data), and _sanitizeInput(input). If _checkDataType has a bug, the entire validation process could fail, leading to incorrect data being processed. Testing _checkDataType directly allows you to isolate and fix the issue quickly. Furthermore, good test coverage of internal functions empowers developers to refactor code with confidence, knowing that existing functionality will remain intact.

Failing to test internal functions leaves your code vulnerable to hidden bugs and makes future maintenance a risky endeavor. Comprehensive testing, including internal function testing, provides a safety net and ensures the long-term stability and reliability of your Node.js modules. Remember that investing in testing upfront saves time and resources in the long run by preventing costly bug fixes and reducing the risk of introducing new issues during development.

Techniques for Accessing Internal Functions

Several techniques exist for gaining access to internal functions in Node.js modules for testing purposes. Each approach has its own trade-offs in terms of invasiveness, maintainability, and best practices. Let’s explore some of the most common methods:

  • Rewiring with rewire or similar libraries: These libraries allow you to modify the module’s internal state, including replacing internal functions with mock implementations or simply accessing them directly. This is a powerful but potentially invasive approach.
  • Exporting Internal Functions (Conditionally): You can modify your module to conditionally export internal functions only when running tests. This approach is cleaner than rewiring but requires modifying the module’s code.
  • Using Dependency Injection: If your module is designed with dependency injection in mind, you can inject mock implementations of internal dependencies during testing, effectively replacing the internal functions with your test doubles.

The “rewire” library is a popular choice for its flexibility. It allows you to get and set private variables and functions within a module. However, it’s important to use it judiciously, as excessive rewiring can make your tests tightly coupled to the module’s internal implementation, making them brittle and prone to breaking with even minor code changes. Conditional exporting, on the other hand, offers a cleaner approach. You can use environment variables or other flags to control whether internal functions are exported. This keeps your test code separate from your production code while still allowing you to access the internal functions when needed. Dependency injection is the most elegant solution, but it requires designing your module with testability in mind from the start.

Choosing the right technique depends on the specific needs of your project and the complexity of your module. Consider the trade-offs between invasiveness, maintainability, and testability when making your decision. Remember that the goal is to write effective tests that provide confidence in your code without making your tests overly complex or brittle.

Practical Examples and Code Snippets

Let’s illustrate these techniques with practical examples. Suppose you have a module calculator.js with the following structure:

// calculator.js const _add = (a, b) => a + b; const calculateSum = (numbers) => { if (!Array.isArray(numbers)) { throw new Error('Input must be an array'); } return numbers.reduce(_add, 0); }; module.exports = { calculateSum, }; 

Here’s how you can test the internal _add function using the rewire library:

// calculator.test.js const rewire = require('rewire'); const calculator = rewire('../calculator'); const assert = require('assert'); describe('Calculator Module', () => { it('should test the internal _add function', () => { const _add = calculator.__get__('_add'); assert.strictEqual(_add(2, 3), 5, 'Internal _add function should return the correct sum'); }); it('should calculate the sum of an array of numbers', () => { assert.strictEqual(calculator.calculateSum([1, 2, 3]), 6, 'calculateSum should return the correct sum'); }); }); 

Alternatively, you could conditionally export the _add function:

// calculator.js const _add = (a, b) => a + b; const calculateSum = (numbers) => { if (!Array.isArray(numbers)) { throw new Error('Input must be an array'); } return numbers.reduce(_add, 0); }; if (process.env.NODE_ENV === 'test') { module.exports._add = _add; } module.exports = { calculateSum, }; 

And the corresponding test:

// calculator.test.js const calculator = require('../calculator'); const assert = require('assert'); describe('Calculator Module', () => { it('should test the internal _add function', () => { if (calculator._add) { assert.strictEqual(calculator._add(2, 3), 5, 'Internal _add function should return the correct sum'); } else { assert.ok(true, 'Skipping internal _add function test (not exported)'); } }); it('should calculate the sum of an array of numbers', () => { assert.strictEqual(calculator.calculateSum([1, 2, 3]), 6, 'calculateSum should return the correct sum'); }); }); 

These examples demonstrate how you can effectively access and test an internal (non-exports) function in a Node.js module using different techniques. Choose the approach that best suits your project’s needs and coding style. Always prioritize clear, maintainable, and reliable tests that provide confidence in your code.

Best Practices and Considerations

When testing internal functions, it’s crucial to adhere to certain best practices to ensure your tests are effective and maintainable. Avoid over-testing internal functions. Focus on testing the critical logic and edge cases within those functions. Testing every single line of code can lead to brittle tests that are difficult to maintain and provide little additional value. Instead, prioritize testing the inputs, outputs, and side effects of the internal functions to ensure they behave as expected.

Strive for a balance between comprehensive testing and maintainability. Write tests that are easy to understand, modify, and debug. Use descriptive test names and clear assertions to make it easy to identify the purpose of each test. Consider using mocking libraries to isolate the internal functions from their dependencies. This allows you to focus on testing the function’s logic without being affected by external factors. Ensure your tests run quickly and reliably. Slow or flaky tests can discourage developers from running them frequently, which can lead to undetected bugs. Use continuous integration tools to automatically run your tests whenever code is changed.

Consider using code coverage tools to measure the percentage of your code that is covered by tests. While 100% code coverage is not always necessary or achievable, it can help you identify areas of your code that are not being adequately tested. However, remember that code coverage is just one metric, and it’s important to focus on writing meaningful tests that provide real value. According to Google’s testing blog, a focus on meaningful testing leads to fewer bugs in production (Google Testing Blog). Remember, the goal is to create a robust and reliable testing strategy that provides confidence in your code and helps you prevent bugs from reaching production.

Infographic here
FAQ: Testing Internal Node.js Functions ---------------------------------------
**Q: Why should I test internal functions?**
A: Internal functions often contain critical logic. Testing them ensures the module's overall reliability and helps prevent bugs from surfacing in production.
**Q: What are some techniques for accessing internal functions in tests?**
A: Common techniques include rewiring with libraries like rewire, conditionally exporting internal functions, and using dependency injection.
**Q: Is it always necessary to test every internal function?**
A: No, focus on testing the critical logic and edge cases within the internal functions. Avoid over-testing, as it can lead to brittle tests.
**Q: What are the best practices for testing internal functions?**
A: Use clear and descriptive test names, keep tests concise and focused, and use mocking libraries to isolate dependencies.
**Q: How can I ensure my tests are effective and maintainable?**
A: Strive for a balance between comprehensive testing and maintainability. Write tests that are easy to understand, modify, and debug.
Testing internal functions in Node.js modules is an essential practice for ensuring code quality and preventing unexpected behavior. By employing techniques like rewiring, conditional exports, or dependency injection, and by following best practices for test design, you can create a robust testing strategy that provides confidence in your code. Remember, comprehensive testing, including testing of internal functions, is a key factor in building reliable and maintainable software.
  • Prioritize testing critical logic within internal functions.
  • Choose a testing technique that balances invasiveness with maintainability.

Ultimately, a well-tested module is a more reliable module. While it may seem daunting at first, mastering the art of testing internal functions unlocks a new level of confidence in your code. It empowers you to refactor with ease, knowing that your tests will catch any regressions. So, embrace the challenge, experiment with different techniques, and build a testing strategy that works for you. By doing so, you’ll not only improve the quality of your code but also become a more proficient and confident Node.js developer. For further reading, consider exploring resources on test-driven development Agile Alliance - TDD. Don’t hesitate to dive deeper and explore advanced testing strategies to elevate your skills and create truly robust applications. Consider exploring advanced testing frameworks like Cypress for end-to-end testing. Learn more about optimizing Node.js modules.

Question & Answer :
I’m trying to figure out on how to test internal (i.e. not exported) functions in nodejs (preferably with mocha or jasmine). And i have no idea!

Let say I have a module like that:

function exported(i) { return notExported(i) + 1; } function notExported(i) { return i*2; } exports.exported = exported; 

And the following test (mocha):

var assert = require('assert'), test = require('../modules/core/test'); describe('test', function(){ describe('#exported(i)', function(){ it('should return (i*2)+1 for any given i', function(){ assert.equal(3, test.exported(1)); assert.equal(5, test.exported(2)); }); }); }); 

Is there any way to unit test the notExported function without actually exporting it since it’s not meant to be exposed?

The rewire module is definitely the answer.

Here’s my code for accessing an unexported function and testing it using Mocha.

application.js:

function logMongoError(){ console.error('MongoDB Connection Error. Please make sure that MongoDB is running.'); } 

test.js:

var rewire = require('rewire'); var chai = require('chai'); var should = chai.should(); var app = rewire('../application/application.js'); var logError = app.__get__('logMongoError'); describe('Application module', function() { it('should output the correct error', function(done) { logError().should.equal('MongoDB Connection Error. Please make sure that MongoDB is running.'); done(); }); });