-
Notifications
You must be signed in to change notification settings - Fork 89
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
Create fluent-assertions library to improve consumer test readability #457
Comments
Draft of RFCThe intent is to add new methods to template.ts in assertions module. These methods all for functional-style chained filtering of resources (and other fields on a template), allowing a test author to more clearly represent what they want to test. Working BackwardsThe proposed change would result in additional content in the existing assertions/README.md: Resource Matching & RetrievalBeyond resource counting, the module also allows asserting that a resource with The following code asserts that the template.hasResourceProperties('Foo::Bar', {
Foo: 'Bar',
Baz: 5,
Qux: [ 'Waldo', 'Fred' ],
}); Alternatively, if you would like to assert the entire resource definition, you template.hasResource('Foo::Bar', {
Properties: { Foo: 'Bar' },
DependsOn: [ 'Waldo', 'Fred' ],
}); -- New content begins -- APIs also exist that allow functional exploration of a template, with // Restrict to only the alarms we care about
const barAlarms = template
.resources()
.filterByType('AWS::Alarm')
.filter({
Properties: { Foo: 'Bar' },
});
expect( barAlarms.size() ).toBe( 4 );
// Property we want to be true on all extracted resources
const foopAlarms = barAlarms.filter({
Properties: { Baz: 'Foop' },
})
expect( barAlarms ).toBe( foopAlarms ); These APIs also allow expression of tests not possible with the simpler // Restrict to only the alarms we care about
const barAlarms = template
.resources()
.filterByType('AWS::Alarm')
.filter({
Properties: { Foo: 'Bar' },
});
expect( barAlarms.size() ).toBe( 4 );
// Property we want to be false on all extracted resources
const foopAlarms = barAlarms.filter({
Properties: { Baz: 'Foop' },
})
expect( foopAlarms ).toBeEmpty(); -- New content ends -- Beyond assertions, the module provides APIs to retrieve matching resources. Public FAQWhat are we launching today?A new set of APIs on the existing Assertions module. Why should I use this feature?This change makes writing readable unit tests easier, and gives a paradigm that developers are familiar with for interacting with their template in tests. Internal FAQWhy are we doing this?As a developer, I frequently find that unit tests I write for CDK stacks are hard to craft and make readable. The statement of what I want to test is often mixed in with setting up the world I expect to exist, and want the test to take as a given. I also find it difficult to assert the negative of some things. For example, it is easy to say a resource exists with given properties, but harder to say a resource does not exist with given properties. Why should we not do this?The same functionality is not available today. The downside to implementing this is that the Template class will have two sets of methods for interacting with resources and other facets. This may confuse users. Is this a breaking change?No |
It seems your proposed API does more or less the same as what Is that right? |
The fluent API gives more capabilities than // Restrict to only the alarms we care about
const barAlarms: { [key: string]: { [key: string]: any } } = template.findResources('AWS::Alarm', {
Properties: { Foo: 'Bar' },
});
expect( Object.keys(barAlarms).length ).toBe( 4 );
// Property we want to be false on all extracted resources
for (const barAlarmKey in barAlarms) {
for (const barAlarm in barAlarms[barAlarmKey]) {
if (barAlarm === "Properties") {
for (const elementKey in barAlarms[barAlarmKey][barAlarm]) {
if (elementKey === "Baz") {
assert(barAlarms[barAlarmKey][barAlarm][elementKey] !== "Foop");
}
}
}
}
} |
@rix0rrr , @MrArnoldPalmer - would either of you be willing to BR this RFC? |
I talked briefly with @rix0rrr about this earlier and here are some things we talked about.
template.resourcePropertiesCountIs('AWS::Alarm', {
'Foo': 'Bar',
'Baz': Match.not('Foop'),
}, 4); I definitely can see the usefulness of making resource filtering multi-step, so you can filter your results multiple times with less code repetition. However, since we haven't seen a lot of requests to add capabilities/apis to the assertions library, and adding these capabilities likely would require some number of development -> testing -> feedback -> iteration cycles, I think developing this in a separate package would be ideal. This also would let us gauge interest from the community/users for inclusion within Would you be willing to create this library and publish it (to all JSII targets) so we can look at it more closely, make suggestions etc, while allowing cdk users to give it a shot as well? If so, documentation should automatically be published to constructs.dev and projen can be used to automate the setup of the repository and publishing actions, which should minimize the amount of procedural stuff you need to do get it going. |
Good call-out. I've only prototyped the APIs in TypeScript so far, and have had issues getting my development environment setup to confirm that changes work with JSII. I've also focused energy on the RFC and BR pieces. I'll keep this in mind as implementation progresses.
Yes. The issue the example is highlighting is that if we assert the subset condition with a separate properties block like this, we run the risk of mismatches between the first and second calls to
Got it. I expect some of the implementation will be duplicative as I won't have access to the internal Would the separate package still be within the aws-cdk project, or initially stand-alone? Before implementing, I'll refresh the RFC based on the new approach (working backwards from a new fluent-assertions package instead of adding to assertions). Are you willing to BR this, @MrArnoldPalmer ? |
Definitely agree with this, though of course to reduce duplication users could do some object const hasTheseProps = { Foor: 'Bar' };
template.resourcePropertiesCountIs('AWS::Alarm', hasTheseProps, 4);
template.resourcePropertiesCountIs('AWS::Alarm', {
...hasTheseProps,
'Baz': 'Foop',
}, 0); Anyway, for now I think this should be vended from a separate repository and published under whatever name you think is best from your npm/maven/pypi/github account, mostly to avoid us blocking you for reviews/feedback during early stage development and iteration. There may be some small duplication (which is fine for now) but you may be able to get pretty far taking a dependency on and extending the public interfaces of I'm happy to BR this proposal but since this will be vended separately I'd say don't worry too much about waiting for our review on anything, but do of course reach out for help/guidance and I will provide what I can. I'm more curious to see the result of what you think is a superior UX/API (with JSII support ideally) than making it fit the general design/standards of the current testing tools in the aws/aws-cdk repo. Even if the result doesn't end up fitting into the core repo/packages, high quality alternatives with different design philosophies are super useful, arguably more so. |
Got it, thanks for the help. |
Marking this RFCs as |
Description
Create a new fluent-assertions library within aws-cdk. This offers fluent chained filtering of resources (and other fields) on a template, allowing a test author to more clearly represent what they want to test.
Roles
Workflow
status/proposed
)status/review
)api-approved
applied to pull request)status/final-comments-period
)status/approved
)status/planning
)status/implementing
)status/done
)The text was updated successfully, but these errors were encountered: