-
Notifications
You must be signed in to change notification settings - Fork 31.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Various issues with assert.snapshot() #44466
Comments
A couple of the issues can be overcome by settings the options. I believe serializing with our v8 serializer would indeed be safer? |
Copying my comment from the other issue: I think we generally just missed this in the original PR and hadn't considered the point you're making here and I think that after I've read your point it I agree with you. We should absolutely change the serialization to not always use So, I consider this a bug in the current implementation (since the results aren't always correct) and I think we should modify this. |
FWIW Jest also has a |
@BridgeAR are you referring to https://nodejs.org/api/v8.html#new-serializer ? |
|
another approach would be to only accept strings, and escape strings that match snapshot delimiters, then we have no serialization |
I think "snapshots generated in newer versions of Node only work in these newer versions" is a fair approach (and if you need to change versions just regenerate the snapshots). A different alternative would be for us to use Jest's serializer (or one of the other userland ones) |
Aside from how restrictive this limitation would be, why does
I am not sure. My understanding of This feature appears sufficiently complicated while easy to implement in userland that I am not sure including it in core is a good idea. |
We would be limited to only accept input that is fully compliant to JSON and that contains no values that are stripped by JSON. Ideally, we serialize as much information as possible to support a wide range of inputs. |
@BridgeAR My comment was in response to the suggestion to only serialize key-value pairs of strings, in which case JSON would be more than enough :) |
I played a bit with moving serialization to the v8.serializer/deserialize API and making it synchronous. I think that:
Some thoughts:
|
Having a sync API is tricky for big snapshots but we could accept that and just document it so that everyone is aware not to save huge files in a snapshot. It is possible to prohibit custom inspect and define all options so that the user's custom settings won't impact the output (we should define the options anyway to guarantee the value is properly stored by inspecting hidden values etc.). So the first three mentioned issues above can all easily be resolved. The main issue I see with
This might not be sufficient as users might be confused in case the output format changed while switching versions and they just refreshed the snapshot and now there seem to be changes even though that's not actually the case. |
Data redundancy leads to inconsistency, which is something an
It seems to me that Finding a simple serialization format suitable for snapshots might be difficult. Jest seems to use CJS files as snapshots, which export the values. The current implementation appears to unconditionally store the snapshot based on the main module path. Isn't this problematic when using test runners such as If this API is going to be complex (custom serializers, where to store snapshots, tooling such as |
Not when I tested it (either with Mocha nor with the native test runner) but it's worth exploring and I think making the path configurable is definitely something we should (eventually) do.
Those things don't seem overly complex honestly compared to most stuff that lives in core and I do think snapshot testing is something users would expect to live in core since we ship a test runner. That said - I think it's totally fine to experiment with it for a while and iron out issues (e.g. fixing serialization) and eventually decide we'd rather vendor it in its own package. I think it'll be easier to estimate the complexity and weigh the trade-off later based on feedback (both from core and from users and adoption). |
Example using mocha, leading to the arbitrary snapshot path colliding for two separate test files: tniessen@ubuntuserver:~/dev/snapshot-problems$ node -v
v18.8.0
tniessen@ubuntuserver:~/dev/snapshot-problems$ cat test1.mjs
import { snapshot } from 'node:assert';
console.log('test1', process.mainModule?.filename, process.argv[1]);
await snapshot({ foo: 'bar' }, 'foo1');
tniessen@ubuntuserver:~/dev/snapshot-problems$ cat test2.mjs
import { snapshot } from 'node:assert';
console.log('test2', process.mainModule?.filename, process.argv[1]);
await snapshot({ foo: 'bar' }, 'foo2');
tniessen@ubuntuserver:~/dev/snapshot-problems$ npx -y mocha test1.mjs
test1 /home/tniessen/.npm/_npx/508606763866ae01/node_modules/mocha/bin/mocha.js /home/tniessen/.npm/_npx/508606763866ae01/node_modules/.bin/mocha
0 passing (1ms)
tniessen@ubuntuserver:~/dev/snapshot-problems$ npx -y mocha test2.mjs
test2 /home/tniessen/.npm/_npx/508606763866ae01/node_modules/mocha/bin/mocha.js /home/tniessen/.npm/_npx/508606763866ae01/node_modules/.bin/mocha
AssertionError [ERR_ASSERTION]: Snapshot "foo2" does not exist
at new AssertionError (node:internal/assert/assertion_error:467:5)
at snapshot (node:internal/assert/snapshot:125:11)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async file:///home/tniessen/dev/snapshot-problems/test2.mjs:3:1
Maybe it should be part of |
That sounds reasonable to me (though unlike your point on serialization and Colin's about a sync API I don't have a strong feeling of where this should live other than it should be experimental) so:
@MoLow @tniessen how does that sound? Additionally - feel free to split those tasks into issues and ask for help - even if we agree on everything it's likely you shouldn't have to be the one to do all the work. |
Souns Ok
Sounds ok to me - but I am not sure this will address all of the concerns raised here (mainly the stability/backwards compatibility of
no strong opinion
in the case of |
I would strongly prefer to serialize the input using JSON (Which is what most userland snapshot libs tend to do) + known extensions than only accepting strings. Other than that I agree with everything you said. |
CC @tniessen is this recap good enough to start working on it? |
Sorry about the delay. I strongly feel that the current API is problematic and does not belong in |
is there any changes with regards to the file format that these snapshots are going to be written into? or did the JSON serialization imply that the snapshot file is going to be in JSON as well? (so not just the snapshot values) my worry is that JSON will make reading multi-line snapshots harder (especially with diffs) |
@intrnl No snapshot API exists at the moment. The serialization format of a future snapshot API, if any, has not been defined. |
I am going to close this issue since the problematic API has been removed and because I am not aware of any related feature requests or development activity. |
@tniessen I was wondering if you thought of a better solution yet? As this would benefit a lot when using big objects. Maybe a similar implementation to jest or vitest? |
Copied from #44429 (comment):
I guess that the made-up serialization format was supposed to be human readable, but it really does not seem appropriate for assertions. I don't expect it to be reliable in the presence of things such as
util.inspect()
potentially omits information, leading to false negatives:util.inspect()
is affected byutil.inspect.defaultOptions
, which means that two snapshots of the same value can be different, leading to false positives.Looking at the change history for
util.inspect()
, it seems that the default options changed between versions. This would potentially also break existing.snapshot
files, leading to false positives.The documentation of
util.inspect()
literally says:Reading and potentially writing a file at some opinionated location during a call to an
assert
API seems very unusual to me, especially because it causes the API to become async. It also seems like this could easily lead to unexpected conflicts.This is particularly problematic when the main module is not the actual test file (e.g., when using
mocha
or other test runners).I understand its purpose, but the addition of a CLI option to "refresh" snapshots (during API calls within
assert
) seems unusual to me (but maybe that's common practice somewhere).Because this is an experimental feature, we can still revert it or modify it to at least address some of these issues.
The text was updated successfully, but these errors were encountered: