-
-
Notifications
You must be signed in to change notification settings - Fork 12
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
How are tests involving enumerating types meant to be written? #29
Comments
The reasoning here is that people rarely care about the type of the collection they pass in, rather they care about the data, and I want to keep the assertions consistent and useful, even though there are some tradeoffs to be made. With 1, 2 | Assert-Equal 1 People favor seeing With types it is a bit more difficult, because there you are asserting on the internal details of how I collect the input. To get correct result you would need to circumvent the pipeline and do: Assert-Type -Actual 1,2 -Expected ([Array]) Or is there a way to get the original collection after the pipeline without anything special being done by the user? Another reason is that assertions over collections can have multiple requirements, sometimes you look for Any item to pass, sometimes you look for all items to pass, sometimes you want the items to be ordered but you don't really care about the data etc. That's why I am trying to make one set of assertions that will deal with "values" and another one that will deal with collections. |
tl;dr I think that most new PowerShell users are surprised by how the pipeline works. The reason for that seems to be deficient courseware.
It seems to me that what we learned from When I was starting out I expected 1,2 | Assert-Equal 1 to fail. "Of course 1,2 | Assert-Equal 1 I expect it to pass for the first record, and fail for the second record. It's apparent that I get that there is popular demand for a testing framework that works in the the way expected by people who haven't learned how PowerShell's pipeline works. I would have preferred that when I was starting out. Thankfully for me though, I suppose I interpreted the words "advanced" and "simplify" in "A set of advanced assertions for Pester to simplify how you write tests" to mean that I feel like we might be miscommunicating about the matter of collections and enumerations and assertions. Here's how I'm thinking about this:
|
I never meant this module to be for advanced users only, the opposite actually. The purpose is to simplify the assertions to behave consistently and predictably. Which is something the current Should assertions in Pester don't do. I think you are reading too much in the collections and enumerators, most people are dealing with arrays and lists 90% of the time, and they expect it to show the whole collection in the assertion output even thought they pass it through pipeline. So the a collection in this sense is something that will be enumerated when passed through pipleline, or won't be wrapped when you do @(). If that works for every type in the universe is not that important, as long as it behaves uniformly and predictably for the most used types. That people don't understand pipeline might be true, but there is not much I can do about it. And I still like writing I was looking at the code the whole day, and I can't really decide how to progress. I would like to merge some of the features with Pester, (especially the formatting), but I am not sure what it will break, and also not sure how to keep it modularized and still use the functions in Pester. :/ |
Per this comment the answer seems to be "no". 😢 |
For posterity, pester/Pester#386 is one real-world example of the confusion caused by the pipeline's conditional enumeration while unit testing. |
@alx9r yeah you are right, now the question is what would be the best way forward. Would getting rid of pipeline completely help to eradicate those problems and make the syntax coherent and less surprising? At what cost? |
Since this debate came up I have been experimenting with an assertion library that assuages this issue (and a number of other pathologies). I expect I'll publish the library at some point. That might be a way forward.
AFAICT avoiding all kinds of unchecked conditional enumeration is critical to meaningful unit tests in PowerShell. It turns out there are a number of conditions where that occurs.
Here's how I see the lay of the land:
In other words, authors able to learn can benefit from an improved assertion library that leads to coherent test code with fewer surprises. Authors unable to learn are probably best served by Pester's FWIW, here is an example of test code written against my experimental library: Describe Example {
$arrayYouThoughWasScalar = @(
[pscustomobject]@{a=1}
[pscustomobject]@{a=1}
)
It 'Verbose style' {
New-TestSubject $arrayYouThoughWasScalar |
Select-InnerObject (Property a) {
$_ | Assert-That -ItIs -EqualTo 1
}
}
It 'Terse style' {
subject $arrayYouThoughWasScalar |
pick (prop a) {
$_ | assert -eq 1
}
}
} The output is as follows: |
This is a lot of very useful information. To me it seems that the most strict solution to avoid the problems would be to reject using the pipeline and differentiate between scalar and "enumerable" assertions. This way input parameters can be rejected (as I do in some cases already) so that:
One big annoyance is that negative numbers become strings when passed as parameters: function a ($a) {
$a.gettype().Name
}
a 1 # int
a -1 # string
a (-1) One way to solve this is trying to cast the value to decimal, but then we should have an option to disable that casting. Or possibly a parameter set can be implemented that defines a well typed parameter (-ExpectedNumber ), or a -Type parameter that specifies what is the type of the input data. All of those are non-intuitive (and it is difficult to detect what the user meant and suggest a better solution - like using a specialized assertion). The thing is that I don't want to get rid of the pipeline input because it is just so convenient. And without it I doubt anyone will ever use this libary, no matter how safe and awesome it is :) So aditionally we could:
In your code example, you are doing pretty much what I envisioned this library to do, except the names are different, and I don't get what the |
Coming from: pester/Pester#386 (comment) In other words, I should (only) get a hint (to add unary comma operator) if the following conditions are true:
The hint will then only be unnecessarily displayed if there are (multiple) objects in the pipeline tested against any of the specific "automatically unrolling" types: |
Putting
It seems you and I have different priorities. My goal is to build an assertion library that is robust to PowerShell's rich behavior. Some of us desperately need such a library, and one currently does not exist. Of course I think it would be good if others can benefit from such a library, but I've decided not to compromise that goal in pursuit of popularity. I found that to be an easy decision because
In practice it's unusual to use
This is just a small sampling; there are many other benefits to using a test fixture for command invocation. Even when no command invocations are involved, the use of |
By "enumerating types" I mean any type for which
[LanguagePrimitive]::GetEnumerator()
exists orIsUnrolledByPipeline
is true.Consider the following tests:
Many of these results are surprising to me:
Assert-Type
were customary PowerShell, it would be checking the type of each element of the array. I get that users who haven't yet learned what|
really does might be surprised if this test failed. But such users are going to experience a lot of pain until they learn that anyway. It seems to me that promoting tools that hide PowerShell's default pipeline behavior just prolongs that painful learning period.[int]
.It strikes me that there are two distinct intentions a user might have for such enumerating types:
And there are two corresponding things the assertions could do:
a. Test the enumerating object.
b. Test the elements the enumerator emits.
Customary PowerShell involving
|
would lead to (a) regardless of the user's intentions. It seems like that would be good behavior for all users for the following reasons:Assert-
is what you'd expect from any other customary advanced function.It seems like (b) should only be done when
|
is involved when the user explicitly expresses their intentions as such. I see the following reasons for this explicitness requirement:Assert
more maintainable because intention (1) is clearly distinguished from intention (2).I can imagine a few ways to implement
Assert
's commands in a manner that clearly couples (1) to (a) and (2) to (b). But I'm wondering if there is a different line of thinking I'm missing for the wayAssert
currently handles enumerating types. How are tests involving enumerating types meant to be written?The text was updated successfully, but these errors were encountered: