Skip to content

Commit

Permalink
Showcase #replace.usingAccessor for DI in the typescript case study (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
fatso83 authored Oct 18, 2023
1 parent c47a4be commit ff0e993
Show file tree
Hide file tree
Showing 8 changed files with 60 additions and 32 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ coverage/
_site
docs/vendor/
vendor/
.bundle
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ tmp/
docs/_site/
docs/js/
docs/assets/js/
vendor
51 changes: 48 additions & 3 deletions docs/_howto/typescript-swc.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,9 +190,11 @@ sandbox.replaceGetter(Other, "toBeMocked", () => stub);

## Use pure dependency injection

### Version 1 : full manual mode

> [Working code][pure-di]
This technique works regardless of language, module systems, bundlers and tool chains, but requires slight modifications of the SUT to allow modifying it. Sinon cannot help with resetting state automatically in this scenario.
This technique works regardless of language, module systems, bundlers and tool chains, but requires slight modifications of the SUT to allow modifying it. Sinon does not help in resetting state automatically in this scenario.

**other.ts**

Expand Down Expand Up @@ -225,11 +227,54 @@ describe("main", () => {
});
```
### Version 2: using Sinon's auto-cleanup
> [Working code][pure-di-with-auto-cleanup]
This is a slight variation of the fully manual dependency injection version, but with a twist to
make it nicer. Sinon 16.1 gained the ability to assign and restore values that were defined
using _accessors_. That means, that if you expose an object with setters and getters for props
you would like to replace, you can get Sinon to clean up after you. The example above becomes much nicer:
**other.ts**
```typescript
function _toBeMocked() {
return "I am the original function";
}

export let toBeMocked = _toBeMocked;

export const myMocks = {
set toBeMocked(mockImplementation) {
toBeMocked = mockImplementation;
},
get toBeMocked() {
return _toBeMocked;
},
};
```
**main.spec.ts**
```typescript
describe("main", () => {

after(() => sandbox.restore())

it("should mock", () => {
mocked = sandbox.fake.returns("mocked");
sandbox.replace.usingAccessor(Other.myMocks, 'toBeMocked', mocked)
main();
expect(mocked.called).to.be.true;
});
```
## Hooking into Node's module loading
> [Working code][cjs-mocking]
This is what [the article on _targetting the link seams_][link-seams-cjs] is about. The only difference here is using Quibble instead of Proxyquire. Quibble is slightly terser and also supports being used as a ESM _loader_, making it a bit more modern and useful. The end result:
This is what [the article on _targetting the link seams_][link-seams-cjs] is about. The only difference here is using Quibble instead of Proxyquire. Quibble is slightly terser and also supports being used as a ESM _loader_, making it a bit more modern and useful. The end result looks like this:
```typescript
describe("main module", () => {
Expand Down Expand Up @@ -261,5 +306,5 @@ As can be seen, there are lots of different paths to walk in order to achieve th
[sut]: http://xunitpatterns.com/SUT.html
[require-hook]: https://levelup.gitconnected.com/how-to-add-hooks-to-node-js-require-function-dee7acd12698
[swc-mutable-export]: https://github.com/fatso83/sinon-swc-bug/tree/swc-with-mutable-exports
[pure-di]: https://github.com/fatso83/sinon-swc-bug/tree/pure-di
[pure-di-with-auto-cleanup]: https://github.com/fatso83/sinon-swc-bug/tree/auto-cleanup-di
[cjs-mocking]: https://github.com/fatso83/sinon-swc-bug/tree/cjs-mocking
4 changes: 4 additions & 0 deletions docs/assets/css/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,10 @@ body {
// Content
.content {
padding: 60px 0px;

& h5 {
font-weight: 600; // make h5 visible: otherwise no difference is shown
}
}

// Pages
Expand Down
2 changes: 1 addition & 1 deletion docs/release-source/release/sandbox.md
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ Usually one intends to _replace_ the value or getter of a field, but there are u

##### Use case: no-frills dependency injection in ESM with cleanup

One use case can be to conveniently allow ESM module stubbing using pure dependency injection, having Sinon help you with the cleanup, without resorting to external machinery such as module loaders or require hooks (see [#2403](https://github.com/sinonjs/sinon/issues/2403)). This would then work regardless of bundler, browser or server environment.
One use case can be to conveniently allow ESM module stubbing using pure dependency injection, having Sinon help you with the cleanup, without resorting to external machinery such as module loaders or require hooks (see [the case study on module mocking Typescript](/how-to/typescript-swc/#version-2-using-sinons-auto-cleanup) for an example). This approach works regardless of bundler, browser or server environment.

#### `sandbox.replaceGetter(object, property, replacementFunction);`

Expand Down
19 changes: 0 additions & 19 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 4 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
"test": "npm run test-node && npm run test-headless && npm run test-webworker",
"check-dependencies": "dependency-check package.json --no-dev --ignore-module esm",
"build": "node ./build.cjs",
"dev-docs": "cd docs; cp -rl release-source releases/dev; npm run serve-docs",
"dev-docs": "cd docs; rsync -r --delete release-source/ releases/dev; npm run serve-docs",
"build-docs": "cd docs; bundle exec jekyll build",
"serve-docs": "cd docs; bundle exec jekyll serve --incremental --verbose --livereload",
"lint": "eslint --max-warnings 101 '**/*.{js,cjs,mjs}'",
Expand All @@ -58,7 +58,8 @@
"prettier:write": "prettier --write '**/*.{js,css,md}'",
"preversion": "./scripts/preversion.sh",
"version": "./scripts/version.sh",
"postversion": "./scripts/postversion.sh"
"postversion": "./scripts/postversion.sh",
"postinstall": "git config --replace-all core.hooksPath scripts/hooks"
},
"nyc": {
"instrument": false,
Expand All @@ -69,7 +70,7 @@
]
},
"lint-staged": {
"*.{js,css,md}": "prettier --check",
"**/*.{js,css,md}": "prettier --write",
"*.js": "eslint --quiet",
"*.mjs": "eslint --quiet --ext mjs --parser-options=sourceType:module"
},
Expand All @@ -92,7 +93,6 @@
"browserify": "^16.5.2",
"debug": "^4.3.4",
"dependency-check": "^4.1.0",
"husky": "^6.0.0",
"lint-staged": "^13.2.0",
"mocha": "^10.2.0",
"mochify": "^9.2.0",
Expand Down Expand Up @@ -137,10 +137,5 @@
"cache": true
},
"mode": "auto"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
}
}
1 change: 1 addition & 0 deletions scripts/hooks/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
npx lint-staged $@

0 comments on commit ff0e993

Please sign in to comment.