views:

189

answers:

10

I'm using Python's built-in unittest module and I want to write a few tests that are not critical.

I mean, if my program passes such tests, that's great! However, if it doesn't pass, it's not really a problem, the program will still work.

For example, my program is designed to work with a custom type "A". If it fails to work with "A", then it's broken. However, for convenience, most of it should also work with another type "B", but that's not mandatory. If it fails to work with "B", then it's not broken (because it still works with "A", which is its main purpose). Failing to work with "B" is not critical, I will just miss a "bonus feature" I could have.

Another (hypothetical) example is when writing an OCR. The algorithm should recognize most images from the tests, but it's okay if some of them fails. (and no, I'm not writing an OCR)

Is there any way to write non-critical tests in unittest (or other testing framework)?

+5  A: 

As a practical matter, I'd probably use print statements to indicate failure in that case. A more correct solution is to use warnings:

http://docs.python.org/library/warnings.html

You could, however, use the logging facility to generate a more detailed record of your test results (i.e. set your "B" class failures to write warnings to the logs).

http://docs.python.org/library/logging.html

Paul McMillan
Both you and Guilherme Chapiewski suggest that unit tests are not the right tool for this job.logging seems an overkill, but maybe warnings could be "good enough" and "simple enough". Gotta experiment with them later.
Denilson Sá
I don't know. None of those warning really looked like they fit. I mean, UserWarning, but that fits everything. That's another import, another dependency, another thing to break. Besides, warnings are for when things are "almost broken". I guess you could test for B and if it fails, throw out the "almost" error, but I feel like that's probably not what the warning module is intended for.
Jonathanb
+3  A: 

Im not totally sure how unittest works, but most unit testing frameworks have something akin to categories. I suppose you could just categorize such tests, mark them to be ignored, and then run them only when your interested in them. But I know from experience that ignored tests very quickly become...just that ignored tests that nobody ever runs and are therefore a waste of time and energy to write them.

My advice is for your app to do, or do not, there is no try.

ryber
+1 for do or not do, never try.
Nona Urbiz
A: 

You could write your test so that they count success rate. With OCR you could throw at code 1000 images and require that 95% is successful.

If your program must work with type A then if this fails the test fails. If it's not required to work with B, what is the value of doing such a test ?

Petar Repac
The value of doing such test is that, if the amount of work required for fixing B is minimum, I can fix it now (or later, with more time). Also, if a later refactor suddenly "fixes" B, it's nice to know.
Denilson Sá
+4  A: 

Asserts in unit tests are binary: they will work or they will fail, there's no mid-term.

Given that, to create those "non-critical" tests you should not use assertions when you don't want the tests to fail. You should do this carefully so you don't compromise the "usefulness" of the test.

My advice to your OCR example is that you use something to record the success rate in your tests code and then create one assertion like: "assert success_rate > 8.5", and that should give the effect you desire.

Guilherme Chapiewski
Although "assert success_rate > 8.5" looks nice at first, such solution hides the actual success_rate.
Denilson Sá
But unit tests aren't usually concerned with specifics (and it's trivial to output the specifics to a text file during the test anyway). Unit tests are black/white, pass/fail sorts of things. They can do that because each test is granular enough that they don't have to be gray, they can be black/white and as you zoom out you get gray.
Jonathanb
+1  A: 

There are some test systems that allow warnings rather than failures, but test_unit is not one of them (I don't know which ones do, offhand) unless you want to extend it (which is possible).

You can make the tests so that they log warnings rather than fail.

Another way to handle this is to separate out the tests and only run them to get the pass/fail reports and not have any build dependencies (this depends on your build setup).

Kathy Van Stone
A: 

Take a look at Nose : http://somethingaboutorange.com/mrl/projects/nose/0.11.1/

There are plenty of command line options for selecting tests to run, and you can keep your existing unittest tests.

GHZ
Sorry, but suggesting to use another testing framework and not explaining how it will solve my question (or even IF it will solve my question) it not very helpful.
Denilson Sá
+1  A: 

From unittest documentation which you link:

Instead of unittest.main(), there are other ways to run the tests with a finer level of control, less terse output, and no requirement to be run from the command line. For example, the last two lines may be replaced with:

suite = unittest.TestLoader().loadTestsFromTestCase(TestSequenceFunctions)
unittest.TextTestRunner(verbosity=2).run(suite)

In your case, you can create separate TestSuite instances for the criticial and non-critical tests. You could control which suite is passed to the test runner with a command line argument. Test suites can also contain other test suites so you can create big hierarchies if you want.

Wim Coenen
Nice! That's almost what I was going to do with warning/logging/print, but your way is a lot cleaner! I like it, gotta test it soon!
Denilson Sá
A: 

Another possibility is to create a "B" branch (you ARE using some sort of version control, right?) and have your unit tests for "B" in there. That way, you keep your release version's unit tests clean (Look, all dots!), but still have tests for B. If you're using a modern version control system like git or mercurial (I'm partial to mercurial), branching/cloning and merging are trivial operations, so that's what I'd recommend.

However, I think you're using tests for something they're not meant to do. The real question is "How important to you is it that 'B' works?" Because your test suite should only have tests in it that you care whether they pass or fail. Tests that, if they fail, it means the code is broken. That's why I suggested only testing "B" in the "B" branch, since that would be the branch where you are developing the "B" feature.

You could test using logger or print commands, if you like. But if you don't care enough that it's broken to have it flagged in your unit tests, I'd seriously question whether you care enough to test it at all. Besides, that adds needless complexity (extra variables to set debug level, multiple testing vectors that are completely independent of each other yet operate within the same space, causing potential collisions and errors, etc, etc). Unless you're developing a "Hello, World!" app, I suspect your problem set is complicated enough without adding additional, unnecessary complications.

Jonathanb
A: 

Thank you for the great answers. No only one answer was really complete, so I'm writing here a combination of all answers that helped me. If you like this answer, please vote up the people who were responsible for this.

Conclusions

Unit tests (or at least unit tests in unittest module) are binary. As Guilherme Chapiewski says: they will work or they will fail, there's no mid-term.

Thus, my conclusion is that unit tests are not exactly the right tool for this job. It seems that unit tests are more concerned about "keep everything working, no failure is expected", and thus I can't (or it's not easy) to have non-binary tests.

So, unit tests don't seem the right tool if I'm trying to improve an algorithm or an implementation, because unit tests can't tell me how better is one version when compared to the other (supposing both of them are correctly implemented, then both will pass all unit tests).

My final solution

My final solution is based on ryber's idea and code shown in wcoenen answer. I'm basically extending the default TextTestRunner and making it less verbose. Then, my main code call two test suits: the critical one using the standard TextTestRunner, and the non-critical one, with my own less-verbose version.

class _TerseTextTestResult(unittest._TextTestResult):
    def printErrorList(self, flavour, errors):
        for test, err in errors:
            #self.stream.writeln(self.separator1)
            self.stream.writeln("%s: %s" % (flavour,self.getDescription(test)))
            #self.stream.writeln(self.separator2)
            #self.stream.writeln("%s" % err)


class TerseTextTestRunner(unittest.TextTestRunner):
    def _makeResult(self):
        return _TerseTextTestResult(self.stream, self.descriptions, self.verbosity)


if __name__ == '__main__':
    sys.stderr.write("Running non-critical tests:\n")
    non_critical_suite = unittest.TestLoader().loadTestsFromTestCase(TestSomethingNonCritical)
    TerseTextTestRunner(verbosity=1).run(non_critical_suite)

    sys.stderr.write("\n")

    sys.stderr.write("Running CRITICAL tests:\n")
    suite = unittest.TestLoader().loadTestsFromTestCase(TestEverythingImportant)
    unittest.TextTestRunner(verbosity=1).run(suite)

Possible improvements

It should still be useful to know if there is any testing framework with non-binary tests, like Kathy Van Stone suggested. Probably I won't use it this simple personal project, but it might be useful on future projects.

Denilson Sá
I'm not sure if marking your answer to your own question as "best answer" is in good tone on Stack Overflow...
Adam Byrtek
Yes, it's okay, there is no problem with that. Actually, yesterday I spent a huge amount of time searching about this at Meta Stack Overflow. See: http://meta.stackoverflow.com/questions/14306/my-improved-answer-based-on-anothers-accepted-answer-for-my-own-question and http://meta.stackoverflow.com/questions/13427/suggestion-for-how-you-could-handle-multiple-helpful-answers and http://meta.stackoverflow.com/questions/13413/how-do-i-combine-two-answers-to-create-the-best-answer-on-stackoverflow/13418#13418
Denilson Sá
A: 

Python 2.7 (and 3.1) added support for skipping some test methods or test cases, as well as marking some tests as expected failure.

http://docs.python.org/library/unittest.html#skipping-tests-and-expected-failures

Tests marked as expected failure won't be counted as failure on a TestResult.

Denilson Sá