Thread overview
Re: Thoughts about unittest run order
May 07, 2019
Jonathan M Davis
May 07, 2019
H. S. Teoh
May 07, 2019
H. S. Teoh
May 06, 2019
On Monday, May 6, 2019 12:13:37 PM MDT H. S. Teoh via Digitalmars-d wrote:
> In theory, the order in which unittests are run ought to be irrelevant. In practice, however, the order can either make debugging code changes quite easy, or very frustrating.
>
> I came from a C/C++ background, and so out of pure habit write things "backwards", i.e., main() is at the bottom of the file and the stuff that main() calls come just before it, and the stuff *they* call come before them, etc., and at the top are type declarations and low-level functions that later stuff in the module depend on.  After reading one of Walter's articles recently about improving the way you write code, I decided on a whim to write a helper utility in one of my projects "right side up", since D doesn't actually require declarations before usage like C/C++ do.  That is, main() goes at the very top, then the stuff that main() calls, and so on, with the low-level stuff all the way at the bottom of the file.
>
> It was all going well, until I began to rewrite some of the low-level code in the process of adding new features. D's unittests have been immensely helpful when I refactor code, since they catch any obvious bugs and regressions early on so I don't have to worry too much about making large changes.  So I set about rewriting some low-level stuff that required extensive changes, relying on the unittests to catch mistakes.
>
> But then I ran into a problem: because D's unittests are currently defined to run in lexical order, that means the unittests for higher-level functions will run first, followed by the lower-level unittests, because of the order I put the code in.  So when I accidentally introduced a bug in lower-level code, it was a high-level unittest that failed first -- which is too high up to figure out where exactly the real bug was. I had to gradually narrow it down from the high-level call through the middle-level calls and work my way to the low-level function where the bug was introduced.
>
> This is quite the opposite from my usual experience with "upside-down order" code: since the low-level code and unittests would appear first in the module, any bugs in the low-level code would trigger failure in the low-level unittests first, right where the problem was. Once I fix the code to pass those tests, then the higher-level unittests would run to ensure the low-level changes didn't break any behaviour the higher-level functions were depending on.  This made development faster as less time was spent narrowing down why a high-level unittest was failing.
>
> So now I'm tempted to switch back to "upside-down" coding order.
>
> What do you guys think about this?

I've run into this a number of times. I generally put the public stuff at the top of the file and the private stuff at the bottom. and as an extension of that, I tend to end up the higher level functions higher up in the file, and the lower level functions lower in the file, potentially triggering the exact problem you're describing. My solution is generally to just start commenting out a bunch of code when I start having test failures with the higher level functions.

In general, I've found that these sort of problems don't come up much when first writing code (rather, it's usually a problem when refactoring). So, I don't usually think about this problem when writing code, and honestly, it would feel really backwards to me to put the lower level and private stuff at the top of the file rather than the bottom, so I just put up with the problem when it comes up. Maybe if I were hitting it all the time, I'd rethink how I organize code, but it hasn't been a big enough problem for me to want to change my approach, and commenting out code generally works quite well when it does come up. Worst case, I copy the code I'm working on into another file until it starts working again.

A similar problem is when I work on something in Phobos that tons of stuff depends on (e.g. std.utf), which can often result in either tests failing elsewhere in Phobos or uild failures, and in such cases, I usually just copy the code elsewhere and get it working before updating the real files.

- Jonathan M Davis



May 07, 2019
On Tue, May 07, 2019 at 08:49:15AM +0000, John Colvin via Digitalmars-d wrote:
> On Monday, 6 May 2019 at 18:13:37 UTC, H. S. Teoh wrote:
> > In theory, the order in which unittests are run ought to be irrelevant.  In practice, however, the order can either make debugging code changes quite easy, or very frustrating. [...]
> 
> Use a test runner that runs all the tests regardless of previous errors?  (and does them in multiple threads, hooray!)

That's certainly one way to go about it.

But perhaps what I'm really looking for is a way to invoke a *specific* unittest (probably identified by starting line number, just like what dmd does to mangle unittests), so that I can iterate on a specific problem case until it's fixed before running through all the tests again.


> https://github.com/atilaneves/unit-threaded
> 
> Then you'll at least get to know everything that failed instead of just whatever happened to be lexically first.
> 
> I agree that some ordering system might improve the time-to-narrow-down-bug-location a bit, but the above might be acceptable nonetheless.

Yeah, not aborting immediately upon test failure would help a lot in this respect.


T

-- 
"If you're arguing, you're losing." -- Mike Thomas
May 07, 2019
On 5/7/19 1:22 PM, H. S. Teoh wrote:
> 
> But perhaps what I'm really looking for is a way to invoke a *specific*
> unittest

unit-threaded. Seriously, it's awesome. Use it. You'll be happy :)

But as far as the default test runner and order of code layout, those are some really interesting points. With a low-level-to-high-level ordering, then in many cases you wouldn't even need to point at a particular test you want to focus on.
May 07, 2019
On Tue, May 07, 2019 at 05:30:03PM -0400, Nick Sabalausky (Abscissa) via Digitalmars-d wrote:
> On 5/7/19 1:22 PM, H. S. Teoh wrote:
> > 
> > But perhaps what I'm really looking for is a way to invoke a *specific* unittest
> 
> unit-threaded. Seriously, it's awesome. Use it. You'll be happy :)

OK, point taken. I'll go check it out. :-P


> But as far as the default test runner and order of code layout, those are some really interesting points. With a low-level-to-high-level ordering, then in many cases you wouldn't even need to point at a particular test you want to focus on.

Well yes, and being the lazy coder that I am, this least-effort path is particularly appealing to me.


T

-- 
WINDOWS = Will Install Needless Data On Whole System -- CompuMan