I certainly like better error/failure formatting. It is a bit of a shame that while it takes the form of “Assert(… Checker)” syntax, it doesn’t maintain any compatibility with gocheck’s actual types. So all of juju/testing/checkers would need to be reworked.
Also, there is the elephant about SetUpTest. Though it seems the pattern is to turn your Suite into a top level Test and then all of your actual tests into c.Run(“subtest” ) calls? Otherwise every one of your Test functions gets these lines:
func TestFoo(t *testing.T) {
c := qt.New(T)
defer c.Done()
c.Defer(setUpSuite(c))
... actual test
}
you probably could put that into a helper to just make it:
func TestFoo(t *testing.T) {
c := setUpSuite(t)
defer c.Done()
...
}
Alternatively it becomes
func TestSuite(t *testing.T) {
c := qt.New(t)
defer c.Done()
c.Run("test1", testOne)
c.Run("test2", testTwo)
c.Run("test3", testThree)
}
Done is automatically called at the end of Run, so you don’t have to repeatedly tear down, but you still need to setUpSuite© at the start of each test.
I can certainly appreciate the idea of Defer(), (in Juju code we had AddCleanup as part of our suites, which does essentially the same thing. Set this up, and then clean it up once the test was done executing, rather than TearDownTest, which is quite unreliable).
As for the other assertion about composition:
// FakeHomeSuite sets up a fake home directory before running tests.
type FakeHomeSuite struct {
CleanupSuite
LoggingSuite
Home *FakeHome
}
There are two things that suites give you, SetUp and extra methods to call. In both cases, I think it is common that you know that you’ll always want both, thus you compose them. For SetUp it is equivalent to:
func setUp(c *gt.C) {
c.Defer(cleanupSetUp)
c.Defer(loggingSetUp)
}
rather than repeating that in every test case you write.
For extra methods, imagine you want to set up fixtures, and then use them, and do that across several different tests. If you aren’t using struct composition, then you end up with
func setUp(c *gt.c) (a, b) {
a := setUpA(c)
b := setUpB(c)
return a, b
}
Maybe that’s functionally better, as it makes it clearer what your dependencies are (they are a fixture object you have to use, rather than a side effect of your suite). And probably it is more of a misfeature to depend on 4+ fixtures (where return values get cumbersome).