Sitelet https://github.com/jestjs/jest/issues/14294
Skip to content

[Feature]: Add support for 'using' keyword to dispose mocks/spies #14294

Description

@tzachbon

🚀 Feature Proposal

Add support for the 'using' keyword, recently introduced in TypeScript 5.2, to handle the disposal of mock resources in Jest. This keyword provides a simple and efficient way to manage resources, and it could greatly enhance the cleanup process of mocks after each test run.

Motivation

The 'using' keyword in TypeScript 5.2 is designed to automatically dispose of anything with a Symbol.dispose function when it leaves scope. Given its utility in managing resources such as file handles and database connections, applying this feature to Jest's mock resources would provide a cleaner and more efficient way to ensure that mocks are properly cleaned up after each test run. This could reduce the risk of memory leaks or other unexpected behavior, enhancing the reliability and performance of tests.

Example

These are just some examples that I would imagine I will use if it will be natively supported from the returned value of "spyOn" or "mock"

describe('...', () => {
	it('...', () => {
		using spy = jest.spyOn(axios, 'get')
	})
})
{

  jest.mock('axios')

	using axios = require('axios')


	describe('...', () => {
		it('...', () => {
			// ...
		})
	})
}
{
	using mocked = jest.mock('axios')

	describe('...', () => {
		it('...', () => {
			// ...
		})
	})
}

Pitch

This feature aligns with the latest practices in TypeScript (and soon JavaScript), providing a more modern and efficient way to manage and dispose of mock resources. Given that Jest is a core tool for testing in the JavaScript ecosystem, it's crucial that it continues to evolve and incorporate these advancements. This feature would not just be a nice-to-have, but rather a significant improvement to Jest's handling of mock resources, making it a valuable addition to the core platform.

Activity

  1. changed the title [-][Feature]: Add support for 'using' keyword to dispose of mocks/spioes[/-] [+][Feature]: Add support for 'using' keyword to dispose of mocks/spies[/+] on Jul 2, 2023
  2. mrazauskas commented on Jul 5, 2023

    @mrazauskas
    Contributor

    Could you elaborate on what you would expect from Symbol.dispose property in each of these cases? What should happen at the end of a block?

  3. SimenB commented on Jul 5, 2023

    @SimenB
    Member

    Something like this is super exciting, but I also don't know if we can really attach semantics here without breaking somebody's workflow

  4. changed the title [-][Feature]: Add support for 'using' keyword to dispose of mocks/spies[/-] [+][Feature]: Add support for 'using' keyword to dispose mocks/spies[/+] on Jul 6, 2023
  5. tzachbon commented on Jul 6, 2023

    @tzachbon
    Author

    Could you elaborate on what you would expect from Symbol.dispose property in each of these cases? What should happen at the end of a block?

    I would expect a full clean-up since the variable will be available only for the block:
    1#

    spyOn(...) {
    	// ...
    	[Symbol.dispose]() {
    	  this.mockReset()
    	}
    }

    I assume it is also relevant for jest.fn()

    2#, 3#
    Restore the request to the original module.

    Something like this is super exciting, but I also don't know if we can really attach semantics here without breaking somebody's workflow

    Why would it break? By using using you are expecting a dispose.
    It won't change the behavior of var, const or let.

  6. mrazauskas commented on Jul 6, 2023

    @mrazauskas
    Contributor

    Thanks. I think it is worth to think if Symbol.dispose could be added to Mock Function which is the return value of both jest.spyOn() and jest.fn().

    But I can’t see it working with the Jest Object which is the return value of jest.mock(). For instance, fake timers are part of the object. Should they be reset too? That’s tricky, because faking time is global operation. If Symbol.dispose on the Jest object resets the state of mocks and fake timers, very soon this can get messy.

    (I talk about fake timers, because node:test added Symbol.dispose support for their timers recently. Reference nodejs/node#48549.)


    In the using axios = require('axios') example the returned object could have Symbol.dispose method already. So if the loaded object will have Symbol.dispose method, it will work without any involvement from Jest side. Would be good not to break that and that’s it.


    Probably it could work with jest.spyOn():

    {
      using spiedMethod = jest.spyOn(someObj, 'someMethod')
    
      // do something
    
      // calls `spiedMethod.mockRestore()`
    }

    Not sure if that makes sense with jest.fn(). One can use const and will be simply garbage collected. Or did I miss something? Can’t see why there would be a need to restore it explicitly (well.. to be precise, Jest is restoring all mock functions after running each test file already).

  7. github-actions commented on Aug 5, 2023

    @github-actions

    This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 30 days.

  8. tzachbon commented on Aug 8, 2023

    @tzachbon
    Author

    @mrazauskas @SimenB - Just making sure, is this something you plan to adopt? Should I consider contributing to this :)

  9. mrazauskas commented on Aug 8, 2023

    @mrazauskas
    Contributor

    is this something you plan to adopt?

    What is this in the sentence above?

    In general it sounds useful. At this moment the concrete use cases are somewhat undefined. I understand it isn't easy to reason about the limitations without digging into the internals of Jest. If you feel like getting your hands dirty, just try putting together a PR.

  10. github-actions commented on Sep 7, 2023

    @github-actions

    This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 30 days.

  11. SimenB commented on Sep 7, 2023

    @SimenB
    Member

    PR welcome, yeah 👍

  12. SimenB commented on Sep 8, 2023

    @SimenB
    Member
  13. SimenB commented on Dec 28, 2023

    @SimenB
    Member

    @tzachbon are you still up for sending a PR here? 🙂

  14. SimenB commented on Feb 20, 2024

    @SimenB
    Member
  15. github-actions commented on Mar 23, 2024

    @github-actions

    This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
    Please note this issue tracker is not a help forum. We recommend using StackOverflow or our discord channel for questions.

  16. locked as resolved and limited conversation to collaborators on Mar 23, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions