-
Notifications
You must be signed in to change notification settings - Fork 7
Conversation
I agree. I like the separate methods. When I skip tests, I usually skip more than one. So I'd like a signature like func (t *T) Skip(count int, description string) An example of my most common use case: if ConditionIsMet() {
t.Ok(Foo(), "foo")
t.Ok(Bar(), "bar")
} else {
t.Skip(2, "condition not met")
} For TODO tests, it'd be nice if users could apply the TODO status to any test function. Right now TODO only makes sense for The first thing that comes to mind is something like this: t.TODO = true
t.Ok(Foo(), "foo")
t.EqOk(Bar(), Baz(), "bar == baz")
t.TODO = false // could be done via defer too or maybe do the same thing internally but hide it behind a callback: t.TODO( func () {
t.Ok(Foo(), "foo")
t.EqOk(Bar(), Baz(), "bar == baz")
}) Having a plain field Preferences? |
The spec [1] suggests "ok 23 # SKIP ...", but if you attempt this with: t.Pass("# SKIP foo") you get an extra hyphen ("ok 23 - # SKIP foo") which is parsed as a successful test (and not a skipped test). Michael gives the following example to motivate 'count' [2]: if ConditionIsMet() { t.Ok(Foo(), "foo") t.Ok(Bar(), "bar") } else { t.Skip(2, "condition not met") } The spec example of this is in [3], showing no special syntax for skipping multiple tests (although there is a special syntax for skipping *all* the tests). Also alphabetize TESTS in the Makefile. [1]: http://testanything.org/tap-version-13-specification.html#skipping-tests [2]: mndrix#6 (comment) [3]: http://testanything.org/tap-version-13-specification.html#skipping-a-few
The spec [1] suggests "not ok 13 # TODO ...", but if you attempt this with: t.Fail("# TODO foo") you get an extra hyphen ("not ok 13 - # TODO foo") which is parsed as a failed test (and not a TODO test). I'd initially written up an alternative to Ok: T.TODO(test bool, description string) but Michael pointed out that future work may add additional test methods (e.g. EqOk) and wanted to support those as well with something like [2]: t.TODO = true t.Ok(Foo(), "foo") t.EqOk(Bar(), Baz(), "bar == baz") t.TODO = false // could be done via defer too or [2]: t.TODO( func () { t.Ok(Foo(), "foo") t.EqOk(Bar(), Baz(), "bar == baz") }) The child-state approach taken in this commit is closer to the latter, but allows for method chaining in the single-test case: t.TODO().Ok(Foo(), "foo") which I find easier to read. The root state is the only one which holds nextTestNumber. It might make more sense if it was *also* the only the state that held Writer (so internally t.root().printf(...) instead of t.printf(...)). However, Writer is public, and we can't keep folks from fiddling with it. Respecting local overrides makes as much sense as anything, although I doubt folks will want to override Writer in their TODO child state. [1]: http://testanything.org/tap-version-13-specification.html#todo-tests [2]: mndrix#6 (comment)
On Wed, Jan 11, 2017 at 04:37:55PM -0800, Michael Hendricks wrote:
func (t *T) Skip(count int, description string)
Done.
For TODO tests, it'd be nice if users could apply the TODO status to
any test function.
This makes sense. I've gone with a child-state approach where:
func (t *T) TODO() *T
gives a child T that has a private .todo flag set. That allows for:
todo := t.TODO()
todo.Ok(Foo(), "foo")
todo.EqOk(Bar(), Baz(), "bar == baz")
t.Ok(…) // continue on with your non-TODO t
and also one-off method chaining:
t.TODO().Ok(Foo(), "foo")
The only thing that makes me uncomfortable with the child-state
approach is that each state entry has its own Writer. If you want to
change writers for both the non-TODO and TODO states, you'll have to
do both:
t.Writer = newWriter
todo.Writer = t.Writer
I'd rather restrict Writer-holding to the root state instance (like
1e8f72e currently does for nextTestNumber), but there's not a clear
way to do that for a public property. Potential approaches:
a. Remove the public Writer from T, use T for the child “states” and
most consuming function signatures, and add a RootT (or some such)
that expands T to expose Writer. Only folks holding that RootT
would be able to adjust the Writer.
b. Replace Writer with a SetWriter setter. Regardless of whether you
call SetWriter on the root state or some ancestor state, you'd
always set the internal .writer on the root state.
c. Leave 1e8f72e as it stands and expect folks to not mess with Writer
unless they've thought it over ;).
And regardless of how we move on this, it may be wise to define a
TAP-harness interface:
type T interface {
func Ok(test bool, description string)
func Fail(description string)
…
}
type t struct {
nextTestNumber int
…
}
to decouple the internal struct from the consumer-facing interface.
Then we could use different stucts for the root state and child states
without consumers having to bother about the distinction.
|
I like this API. It works well for both one-off and multiple TODO tests, as you've outlined. I often "comment out" a block of tests. In that case, I like the diff to show just the new "comment" lines. Like this: +t = t.TODO()
t.Ok(Foo(), "foo")
t.EqOk(Bar(), Baz(), "bar == baz")
+t = t.Done()
t.Ok(…) // continue on with your non-TODO t That leaves "git blame" output pointing at the commits which originally created As far as implementation goes, the parent-child semantics complicate it more than I'd like. I think we can retain the nice API you've created but use a copy semantics instead. Namely: "TODO() returns a derivative T in todo mode, sharing all other state". If func (t *T) TODO() *T {
newT := *t
newT.todo = true
return &newT
}
func (t *T) Done() *T {
newT := *t
newT.todo = false
return &newT
}
I'm OK with this :-). If we get to the point where a T interface becomes obviously necessary, I think we can revisit it at that point. |
The spec [1] suggests "not ok 13 # TODO ...", but if you attempt this with: t.Fail("# TODO foo") you get an extra hyphen ("not ok 13 - # TODO foo") which is parsed as a failed test (and not a TODO test). I'd initially written up an alternative to Ok: T.TODO(test bool, description string) but Michael pointed out that future work may add additional test methods (e.g. EqOk) and wanted to support those as well with something like [2]: t.TODO = true t.Ok(Foo(), "foo") t.EqOk(Bar(), Baz(), "bar == baz") t.TODO = false // could be done via defer too or [2]: t.TODO( func () { t.Ok(Foo(), "foo") t.EqOk(Bar(), Baz(), "bar == baz") }) This commit adds a .TODO boolean following Michael's first proposal. It also adds a Todo() method returning a copy of the test-state (but setting TODO), which you can use to avoid altering the original test-state's TODO value. This can be useful when you don't want to bother with a defer, or your TODO tests all live in an existing branch of a function, or you want to chain methods for a one-off TODO test: t.TODO().Ok(Foo(), "foo") To keep the count synchronized between sibling test states, nextTestNumber is now a pointer. We don't do anything to keep Writer synchronized, because it's already part of the public API as a non-pointer, and Michael is ok leaving this up to the caller [3]. [1]: http://testanything.org/tap-version-13-specification.html#todo-tests [2]: mndrix#6 (comment) [3]: mndrix#6 (comment)
On Thu, Jan 12, 2017 at 03:36:01PM -0800, Michael Hendricks wrote:
+t = t.TODO()
t.Ok(Foo(), "foo")
t.EqOk(Bar(), Baz(), "bar == baz")
+t = t.Done()
t.Ok(…) // continue on with your non-TODO t
This is basically your initial propopsal [1], so I've pushed 1e8f72e →
967e479 which adds a public TODO attribute. I've kept the chainable
method (now returning a sibling state instead of a child state), but
changed its name to ‘Todo()’.
[1]: #6 (comment)
|
Nicely done. Thanks. |
The spec wants these without hyphens, so we can't generate them without a new API. You could have
T.Directive(test bool, directive, description string)
, but it seems more convenient to have a separate method for each case.