Skip to content
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

fix: display mismatch reasons in "following elements were mismatched" #1362

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

vlsi
Copy link
Collaborator

@vlsi vlsi commented Mar 18, 2023

Fixes #1359


I confirm that I have read the Contributor Agreements v1.0, agree to be bound on them and confirm that my contribution is compliant.

Comment on lines +145 to +148
// TODO: assertionCreatorOrNull should be non-nullable, and this "to be null" should be a part of expectation
assertionBuilder.createDescriptive(DescriptionBasic.TO_BE, Text.NULL) {
e == null
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is strange to handle e == null here. I wish assertionCreatorOrNull was non-nullable.

Copy link
Owner

@robstoll robstoll Mar 22, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, this functionality could be refactored. Is due to some legacy behaviour introduced in Atrium 0.2.0 or the like and wouldn't be necessary any more. Going to create a discussion about it

@vlsi
Copy link
Collaborator Author

vlsi commented Mar 18, 2023

The fix makes

        expect(listOf(User("joe", "qwerty"), User("j", "qwerty1"))) {
            toHaveElementsAndAll {
                feature("login", { login })
                    .feature("size", { length })
                    .because("login should be longer than one letter") {
                        toBeGreaterThan(1)
                    }
                feature("password", { password })
                    .because("password should be secure") {
                        notToEqual("qwerty")
                    }
            }
        }

to produce

I expected subject: [User(login=joe, password=qwerty), User(login=j, password=qwerty1)]        (java.util.Arrays.ArrayList <1937662030>)
◆ elements need all: 
    » ▶ login: 
        ◾ ▶ size: 
            ◾ to be greater than: 1        (kotlin.Int <987792975>)
            ℹ because: login should be longer than one letter
    » ▶ password: 
        ◾ not to equal: "qwerty"        <952055826>
        ℹ because: password should be secure
    ❗❗ following elements were mismatched: 
       ⚬ ▶ index 0: User(login=joe, password=qwerty)        (ch.tutteli.atrium.api.fluent.en_GB.samples.ThrowableFeatureExtractorSamples$messageFeature2$User <1635068416>)
           ◾ ▶ login: "joe"        <1130906588>
               ◾ ▶ size: 3        (kotlin.Int <439323450>)
                   ◾ to be greater than: 1        (kotlin.Int <987792975>)
                   ℹ because: login should be longer than one letter
           ◾ ▶ password: "qwerty"        <952055826>
               ◾ not to equal: "qwerty"        <952055826>
               ℹ because: password should be secure
       ⚬ ▶ index 1: User(login=j, password=qwerty1)        (ch.tutteli.atrium.api.fluent.en_GB.samples.ThrowableFeatureExtractorSamples$messageFeature2$User <1133787119>)
           ◾ ▶ login: "j"        <44754983>
               ◾ ▶ size: 1        (kotlin.Int <987792975>)
                   ◾ to be greater than: 1        (kotlin.Int <987792975>)
                   ℹ because: login should be longer than one letter
           ◾ ▶ password: "qwerty1"        <1254614872>
               ◾ not to equal: "qwerty"        <952055826>
               ℹ because: password should be secure

The following should probably be hidden:

           ◾ ▶ login: "joe"        <1130906588>
               ◾ ▶ size: 3        (kotlin.Int <439323450>)
                   ◾ to be greater than: 1        (kotlin.Int <987792975>)
                   ℹ because: login should be longer than one letter

...

           ◾ ▶ password: "qwerty1"        <1254614872>
               ◾ not to equal: "qwerty"        <952055826>
               ℹ because: password should be secure

Copy link
Owner

@robstoll robstoll left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The output already looks good 👍 but the implementation needs to be adopted

@@ -63,8 +63,7 @@ abstract class BaseExpectImpl<T>(
representationInsteadOfFeature?.let { provider ->
maybeSubject.fold({ null }) { provider(it) }
} ?: maybeSubject.getOrElse {
// a RootExpect without a defined subject is almost certain a bug
Text(SHOULD_NOT_BE_SHOWN_TO_THE_USER_BUG)
Text.EMPTY
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't change this in the end, if you saw this text in reporting, then it is most likely an implementation bug 😉

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, what is your recommendation then?
Currently, Atrium uses an explicit None subject in order to render elements need all: block.:

it.collectAssertions(container, None, assertionCreatorOrNull)

@@ -61,6 +61,6 @@ class TextFeatureAssertionGroupFormatter(
parameterObject: AssertionFormatterParameterObject
) : AssertionGroup by assertionGroup {
override val description: Translatable = newName
override val representation: Any = if(parameterObject.isNotInExplanatoryFilterGroup()) assertionGroup.representation else Text.EMPTY
override val representation: Any = assertionGroup.representation
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please revert in the end

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What would be your suggestion then?

Apparently, the current implementation in Atrium is "whatever is displayed in explanatory goes WITHOUT representation".
At the same time, the only way to put "warning sign" is to wrap it in explanatory.

That creates a deadlock, and I think it is wrong that "explanatory" implies "remove all representations". I just do not understand why it removes the representations. I believe the ones who want to display message "without representation" should call the relevant APIs "without representation", and it should not be "explanatory" that hides representations.

Copy link
Owner

@robstoll robstoll Mar 22, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is wrong that "explanatory" implies "remove all representations"

The reason why we remove the representation is because usually we explain what expectation was defined without it being bound to a subject. For instance, the ◆ elements need all: part. The expectations shown there are not bound to a specific element in the Iterable.

At the same time, the only way to put "warning sign" is to wrap it in explanatory.

I see now better where your request for a non-fluent assertionBuilder comes from :)


I see overlapping topics here. I think one way forward would be that we can define that a certain Assertion should be shown as is within an explanatory group, i.e. kind of escape the explanatory mechanism. This way we would also only show failing expectations etc. as it was not within an explanatory group.

Another approach would be that we allow more freely what bullet points are used and then you would not use an explanatory group in your case but just a regular list group with another bullet point.

Have to go, wil, think about it later on.

Copy link
Owner

@robstoll robstoll Mar 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to take the approach with escaping because otherwise we loose context. for this to work, we need:

  • change from NonFiltering count to FilteringStack
  • A new GroupAssertionType EscapeNonFiltering which behaves like an invisible group but additionally modifies the stack

I can give further pointers in case you want to tackle this. I am writing on top of my head, would need to take a closer look regarding the exact places and names.

Comment on lines +141 to +157
val mismatches = list.mapIndexedNotNull { index, e ->
val assertion = if (assertionCreatorOrNull != null) {
container.collectBasedOnSubject(Some(e as E), assertionCreatorOrNull)
} else {
// TODO: assertionCreatorOrNull should be non-nullable, and this "to be null" should be a part of expectation
assertionBuilder.createDescriptive(DescriptionBasic.TO_BE, Text.NULL) {
e == null
}
}

assertion.takeIf { mismatchIf(it.holds()) }?.let {
assertionBuilder
.feature
.withDescriptionAndRepresentation(TranslatableWithArgs(INDEX, index), e)
.withAssertion(it)
.build()
}
Copy link
Owner

@robstoll robstoll Mar 22, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should do the modification within createIndexAssertions and not here - I suggest you rename createIndexAssertions to createMismatch as it is used only for this purpose

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of course, it should replace the current createIndexAssertions, however, I tried to implement something minimal to see what are other changes required.

One of the takeaways, for now, is the current implementation shows both successful and failing assertions, and it does not distinguish between successful and failing ones.
For instance login: "joe" was successful, however, it was displayed the same as password: "qwerty" which failed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

"following elements were mismatched" hides the reason for the failure
2 participants