denoland / denoland/std

Refactor-Safe Mocks / Stubs that Allow Renaming

Open
#6,818 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
3.6k
Forks
681
PR merge metrics
No merged PRs in 30d

Description

## **Is your feature request related to a problem? Please describe.**

Currently we get type hinting when stubbing like having autocomplete for instance methods/properties on an object. But if those names are ever refactored the test code does not update propertly.

## **Describe the solution you'd like**

I would love to see a solution like:
-

This binds typing to the original class, so refactors like renaming then update throughout tests.

### Example
```ts
describe('Party Tests', () => {
test('Mock out an interface', () => {
const mock = mock();
mock.start('disco party');

expect(mock.start).toHaveBeenCalledWith('disco party');
});

test('mock out a return type', () => {
const mock = mock();
mock.getPartyType.mockReturnValue('west coast party');

expect(mock.getPartyType()).toBe('west coast party');
});
```

## **Describe alternatives you've considered**

### `as unknown as Class` Pattern

We've tried the `as unknown as Class` pattern, but it's ugly and doesn't necessarily solve this problem.
This allows us to push partially mocked instances/classes through dependency injection.

```ts
export function createMockClient(): SomeClient {
return {
getWords: () => Promise.resolve(undefined),
} as unknown as SomeClient;
}
```

### TS Mockito

-

This library is fairly great, but it's not well maintained, and I would prefer to have something out-of-the-box with deno. I've also never been a huge fan of the `when` `then` pattern that it utilizes for stubbing, but it's modeled after Java Mockito and I imagine it's fairly opinionated. I've seen reasons of the `when` `then` pattern, but in my experience it just causes confusion during testing when something doesn't get called even though you're calling it. I would rather setup for a test, and if something hasn't been mocked it could error out. Maybe better messaging is key. I know the `when` pattern helps set expectations, but I would rather just "expect" them in my test and validate what was called.

### Strong Mock
-

Seems promising, very much like ts-mockito, just not a lot of adoption yet it seems, but at least it seems to be maintained.

### Other Thoughts

Ultimately typesafe testing in Deno feels a bit janky, between potentially lock of documentation/examples, and fighting the ES6 import system. I'm used to being able to mock out imports before a library brings the dependency in. I feel that you shouldn't have to fight the system in place, that you should be able to lean into your underlying basics of JS, you shouldn't need to introduce patterns like dependency injection (even though they are great patterns that help testing), you shouldn't need to reinvent a dependency injection container when ES6's import system is good enough.

I know there's a "partial mocking" section in the documentation, but I would rather have an empty shell that I explicitly mock out parts that are needed, rather than take a working instance and individually mocking out parts. This seems like it opens you up to potential issues with actual code in place. https://docs.deno.com/examples/mocking_tutorial/#partial-mocking This also still doesn't solve the renaming issue.

### Other Things I've Looked Into

-
- not maintained, bus factor of one

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.