#23663 closed Cleanup/optimization (fixed)
Commands classes are not properly testable due to their initialization in execute() method!
Reported by: | Davide Zanotti | Owned by: | nobody |
---|---|---|---|
Component: | Core (Management commands) | Version: | 1.7 |
Severity: | Normal | Keywords: | test, tdd, management, commands |
Cc: | Loic Bistuer, berker.peksag@… | Triage Stage: | Ready for checkin |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
I was writing a series of tests for a custom command, but I get:
AttributeError: 'Command' object has no attribute 'stderr
I realized that the problem is that references to "stderr" and "stdout" are initialized in the execute() method!
This makes commands classes untestable, or better, I can write tests but tests that are not unitary, this is against the principles of TDD, I should not run a command to test it, instead I should write tests for each single little piece of logic that makes my command do the job.
Please, move initialization logic into init so we can test commands methods separately.
Change History (11)
comment:1 by , 10 years ago
comment:2 by , 10 years ago
Cc: | added |
---|
Alternatively, you can use io.StringIO
instead of OutputMock
.
import io class SpamTest(TestCase): def setUp(self): self.command = Command() self.command.stdout = io.StringIO() self.command.stderr = io.StringIO()
This way, you can also test the output easily (e.g. something like self.assertFalse(self.command.stderr.getvalue())
).
comment:3 by , 10 years ago
Easy pickings: | unset |
---|---|
Has patch: | set |
Triage Stage: | Unreviewed → Accepted |
PR https://github.com/django/django/pull/3391 addresses this in one of the individual commits.
comment:4 by , 10 years ago
Patch needs improvement: | set |
---|
I'd really like to address this issue in a separate pull request. Moreover the commit you reference is a mix of code and stylistic changes, which makes it hard to review, especially in tests. Could you please work on that separation?
comment:5 by , 10 years ago
Patch needs improvement: | unset |
---|---|
Triage Stage: | Accepted → Ready for checkin |
comment:6 by , 10 years ago
@berkerpeksag patching stderr/stdout
as suggested doesn't allow setting no_color
which is very useful in tests.
@claudep sorry I didn't notice your comment earlier. Yes the stylistic changes belonged to another commit, it's now done. I was mostly using the PR for CI which gave me a really bad time (turns out Jenkin's doesn't run in a TTY). I can still make a PR just for this fix if you want.
comment:8 by , 10 years ago
Loïc, thanks, that's easier to review!
Traditionally, we have favored two interfaces to management commands: the command line and call_command
. I understand the need for fine-grained unit testing, I just hope this won't entice users to run commands in new "imaginary" ways... Whatever, go for it!
comment:9 by , 10 years ago
Resolution: | → fixed |
---|---|
Status: | new → closed |
Currently I bypassed the issue by mocking stdout and stderr:
class OutputMock(object):
def setUp(self):