-
Notifications
You must be signed in to change notification settings - Fork 1.4k
[Idea]: test macros #695
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
Comments
@jfmengels points out another potential benefit of this in our linter: avajs/eslint-plugin-ava#56 (comment) |
I love the idea for test macros, but I noticed that it will also make linting some stuff harder (finding out that |
I am not sure if Eslint has an equivalent to Babel's bindings. It would make linting macros pretty easy. I have not seen anything in the docs though. |
@jfmengels - What if, instead of storing a reference, you had to pass a string identifier, and it became just another macro. test.macro('myMacro', (t, input, expected) => ...);
test.myMacro('input', 'expected');
test.only.myMacro('input', 'expected'); I think that makes static analysis a little easier (especially if you define macros in helpers). |
Interesting idea, but then you'd have
An alternative would be // Proposal 1
// Defining
test.defineMacro('myMacro', (t, input, expected) => ...);
// Using
test.macro('myMacro', 'input', 'expected'); But you'd still have problem 1 above. // Proposal 2
// Defining (call it createMacro, defineMacro, whatever)
const myMacro = test.defineMacro((t, input, expected) => ...);
// Using the macro
test.macro('title', myMacro, 'input', 'expected');
// Proposal 3
// or even alternatively, by re-using `test`, that adapts its behavior
const myMacro = test.macro((t, input, expected) => ...);
// Using
test('title', myMacro, 'input', 'expected'); I think proposal 2 is the best implementation. You get
|
My only concern with your proposals is the verbosity. I was already concerned about that with my second proposal here, your proposals just make it worse. Ideally, we could use my first proposal, as it is the least verbose. My second one was just to address the static analysis problem. IMO, most of your concerns aren't that big a deal:
Exactly - it won't affect other tests (because we fork), so no surprising behavior. You define which macros you load in each test (so no trouble figuring out what is happening).
I think in the case of a helper defining macros - the user has decided to do that themselves. AVA's not doing that to them. They won't be surprised by it. In general, I agree with your sentiment, but this seems like a reasonable exception.
We could protect against macros that try to overwrite built in properties. |
Sure, but still, I myself as a user don't like it. And if AVA doesn't allow macros to be used without a variable, AVA will kind of force me to do that, should I want to create a macro used in multiple files. It's more verbose, but not that much, but that's just my opinion. If you're that concerned about verbosity, my proposal 3 is pretty short. It can be even be shortened by simply having macro be a simple function and injecting the arguments // Normal tests
test(t => { var a = 2, b = 3; ... });
test(t => { var a = 2, b = 4; ... });
// Proposal 4
// Macro/reusable test
var macro = (t, a, b) => { ... };
test(macro, 2, 3);
test(macro, 2, 4); Importing would be easy, as it's a special function. No naming, no AVA tampering. Alternatively, args could be added to the context: |
Hmm. Proposal 4 looks pretty good, and it should be really simple to implement. I like it. |
👎 You can completely replace |
Proposed it just in case proposal 4 could not be done because test already uses further arguments or so. Much prefer the args in function option too. |
Rather than spreading the arguments ourselves, we should pass additional args as an array: function macro(t, additionalArgs) {
if (additionalArgs.length === 2) {
// ...
} else {
// ...
}
}
test(macro, 'foo', 'bar');
test(macro, 'foo'); This allows us to pass additional args in the future breaking changes. With destructuring, defining your macro isn't really any more verbose: function macro(t, [foo, bar]) {
// ...
} |
👍 on proposal 4. Perhaps we could expose the macro creating functionality through an |
@novemberborn With proposal 4 macros are simple functions, and there's no need for a special macro creator. |
Ah, I like it even more now! |
What separates proposal 4 ( |
Nothing, except that the arguments may be used by the macro (I might not have understood the question right).
Since the macro is simply a function, no. I'm guessing it might be wanted in some cases (if you're testing plenty of cases for I'd argue that it's best if the macro doesn't set a title, and that it should be given to the test function, like function addMacro(t, a, b, expected) {
t.is(add(a, b), expected);
}
test('add 2 numbers', addMacro, 2, 3, 5);
test('add a positive and a negative number', addMacro, 2, -3, -1);
test('add a number and NaN', addMacro, 2, NaN, NaN);
Though I see the value in it for the user, one of the points for macros was to make static analysis easier/possible. I'd say that having macros that could create tests (potentially containing |
There would be no such thing as "too many arguments". Everything past the test function would be passed as "additional arguments". The macro function itself would get an array as a second argument, containing all the "additional arguments"
I was thinking about titles too. Here was my thought: function macroFn(t, ...additionalArgs) {
t.is(...);
}
// optional: attach a title generator to `macroFn`
macroFn.title = function (...additionalArgs) {}
// optional: attach a title template to `macroFn` (not sure which template language to use)
macroFn.title = '$1 does thing $2'
// title is extracted from string:
test.macro('title', macroFn, ...additionalArgs);
// title generated using titleFn or template
test.macro(macroFn, ...additionalArgs);
I think we could support it in a limited way by allowing arrays of macros: function aPlusB(t, [a, b, expected]) {
var result = calculator.parse(`${a} + ${b}`);
t.is(result, expected);
}
aPlusB.title = "${1} + ${2} === ${3}";
function bPlusA(t, [a, b, expected]) {
var result = calculator.parse(`${b} + ${a}`);
t.is(result, expected);
}
bPlusA.title = "${2} + ${1} === ${3}";
const addition = [aPlusB, bPlusA];
// these two lines create 4 total tests
test(addition, 3, 4, 7);
test(addition, 2, 2, 4); |
Can you run a macro using just I like the |
I think just |
If we can avoid introducing the idea of I think the suggestion for 👍 For the title template, even though when given complex arguments, it will be unreadable, but not a lot of things we can do about that? 👍 on the array proposal too. Still not sure why you think the test should be given an array of args Also, do you think |
Sure. It means that macros aren't anything specific, we're just giving the test implementation more powers. |
That's why ... allowing for the possibility of a third argument. |
This adds basic macro support as discussed in avajs#695. It does not completely implement the spec outlined there, specifically: - The macro can only specify the title using a function in `macroFn.title`. We discussed allowing `macroFn.title` to also be a string, and allowing some form of template language for extracting a title. However, using ES2015 string templates is already pretty easy, so we may just skip this. - We discussed allowing groups of tests to be created using arrays. Both the above proposals are found in [this comment](avajs#695 (comment)). They both enhance the implementation found in this commit, and would not break the contract. So I don't think there is anything preventing us from shipping this now.
This adds basic macro support as discussed in avajs#695. It does not completely implement the spec outlined there, specifically: - The macro can only specify the title using a function in `macroFn.title`. We discussed allowing `macroFn.title` to also be a string, and allowing some form of template language for extracting a title. However, using ES2015 string templates is already pretty easy, so we may just skip this. - We discussed allowing groups of tests to be created using arrays. Both the above proposals are found in [this comment](avajs#695 (comment)). They both enhance the implementation found in this commit, and would not break the contract. So I don't think there is anything preventing us from shipping this now.
* Basic macro support. This adds basic macro support as discussed in #695. It does not completely implement the spec outlined there, specifically: - The macro can only specify the title using a function in `macroFn.title`. We discussed allowing `macroFn.title` to also be a string, and allowing some form of template language for extracting a title. However, using ES2015 string templates is already pretty easy, so we may just skip this. - We discussed allowing groups of tests to be created using arrays. Both the above proposals are found in [this comment](#695 (comment)). They both enhance the implementation found in this commit, and would not break the contract. So I don't think there is anything preventing us from shipping this now. * fix readme indentation * spread arguments * Allow arrays of macros * pass providedTitle as first argument to title function. * improve docs
Macros shipped in https://github.com/avajs/ava/releases/tag/v0.15.0. |
From: #78 (comment)
Occasionally it's useful to create a function that calls
test
for you:Unfortunately, such helpers create a situation where it becomes basically impossible to do static analysis on the code (static analysis may be necessary to solve #78).
I think we could solve some of this with a
macro
command:The text was updated successfully, but these errors were encountered: