Re: Minor feature: Make TestCase pass the testcase instance to self.client's constructor
On 5 January 2013 05:23, Malcolm Boxwrote: > > The general pattern I want to implement is have a test client that makes > assertions about all the requests made during a set of tests. For example, it > could check that every get() returned cache headers, or that content_type is > always specified in responses. Or that the response has certain HTML, uses > certain templates etc - ie all of the assertion testing goodness in > django.test.TestCase. > > My concrete use case is a JSONClient that adds a json_get / json_post methods > which makes sure to setup content_type etc on the call, and then validates > that what came back was valid JSON. > > The simple, wrong way is to do: > > def check_response(self, response): >self.assertContains(response, ) > > > def test(): >r = self.client.get(...) >self.check_response(r) > > but this is error prone, verbose etc etc. > > The right thing is that within a test suite, the get()/post() etc to do the > checks for me - and so it should be possible to create a testclient that does > this, and be able to use this testclient in various test suites. I don't know that an appeal to authority is useful here, but I disagree with your "wrong" and "right" assessments. Have you heard of the concept of the "four phase test"? http://xunitpatterns.com/Four%20Phase%20Test.html One of the things about tests is that they need to be understandable at a glance. Having the "exercise" and "verify" steps clearly separated helps with this IMHO, and the approach you're proposing doesn't really lend itself to this. (If your local library or a friend or someone has the book "xUnit Test Patterns", the first 150 pages or so are extremely well worth reading) Cheers, mwh -- You received this message because you are subscribed to the Google Groups "Django developers" group. To post to this group, send email to django-developers@googlegroups.com. To unsubscribe from this group, send email to django-developers+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.
Re: Minor feature: Make TestCase pass the testcase instance to self.client's constructor
Hi again Malcolm, I think making assertions in the test client is wrong for most situations, though I can't (of course) say much about your specific case; so I'm -1 on adding the testcase as a client initializer argument (this would send the message that such things are encouraged). However, note that the only use of the client_class attribute in the Django code is in the simple invocation: self.client = self.client_class() which means you can achieve your goal, with no change to Django code, using this (admittedly, a little abusive) hack: class MyTestBase(TestCase): def client_class(self): return MyTestClient(self) If your own code refers to self.client_class and expects to have access to its attributes, you can still arrange it with a little more effort (an elaborate and transparent way, using a descriptor, and a simple way, using another attribute, are both left as an exercise to the reader). HTH, Shai. -- You received this message because you are subscribed to the Google Groups "Django developers" group. To post to this group, send email to django-developers@googlegroups.com. To unsubscribe from this group, send email to django-developers+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.
Re: Minor feature: Make TestCase pass the testcase instance to self.client's constructor
On Sat, Jan 5, 2013 at 9:11 AM, Shai Bergerwrote: > On Friday 04 January 2013, Malcolm Box wrote: > > > > The general pattern I want to implement is have a test client that makes > > assertions about all the requests made during a set of tests. For > example, > > it could check that every get() returned cache headers, or that > > content_type is always specified in responses. Or that the response has > > certain HTML, uses certain templates etc - ie all of the assertion > testing > > goodness in django.test.TestCase. > > > > > > def test(): > >r = self.client.get(...) > >self.check_response(r) > > > > but this is error prone, verbose etc etc. > > > > The right thing is that within a test suite, the get()/post() etc to do > the > > checks for me - and so it should be possible to create a testclient that > > does this, and be able to use this testclient in various test suites. > > > > No, you're mixing concerns. > > > Is there a simple, clean way to solve this that I'm missing? > > > > class MyTestCase(TestCase): > > def check_response(self, response): > self.assertContains(response, ) > > def checked_get(self, *args, **kw): > r = self.client.get(*args, **kw) > self.check_response(r) > return r > > class SpecificTest(MyTestCase): > > def test(): > r = self.checked_get(...) > ... > > That's how I would do it. > > Which is lovely, right up to the point where someone writes a test with: self.client.get() Extremely likely to occur (in fact 100% probable in any sizeable test suite), won't show up as an error and yet suddenly the tests aren't testing what they should be. I don't understand your "mixing concerns" argument - the concern of the TEST client should be testing just as much as it's the concern of the TESTcase and TESTsuite. The hint is in the name :) Now if the test client was truly decoupled (ie expected to be used in other places) then I'd totally agree with you/Russ about mixing concerns and coupling. But AFAIK it's not - it's explicitly a utility that does all sorts of jiggery-pokery to support testing. Adding a parameter to the initialiser seems a very minor sin (if a sin at all) in comparison. However, if I'm not convincing anyone I'll shut up about this - the "fix" is simply: class Tests(TestCase): def setUp(self): self.client = MyBetterTestClient(self) (although this does then require any derived test case to remember to call super(Derived, self).setUp(*args, **kw)) I'd just have liked to be able to do that using the existing machinery: class Tests(TestCase): client_class = MyBetterTestClient Cheers, Malcolm -- You received this message because you are subscribed to the Google Groups "Django developers" group. To post to this group, send email to django-developers@googlegroups.com. To unsubscribe from this group, send email to django-developers+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.
Re: Minor feature: Make TestCase pass the testcase instance to self.client's constructor
On Friday 04 January 2013, Malcolm Box wrote: > > The general pattern I want to implement is have a test client that makes > assertions about all the requests made during a set of tests. For example, > it could check that every get() returned cache headers, or that > content_type is always specified in responses. Or that the response has > certain HTML, uses certain templates etc - ie all of the assertion testing > goodness in django.test.TestCase. > > My concrete use case is a JSONClient that adds a json_get / json_post > methods which makes sure to setup content_type etc on the call, and then > validates that what came back was valid JSON. > > The simple, wrong way is to do: > > def check_response(self, response): >self.assertContains(response, ) > > > def test(): >r = self.client.get(...) >self.check_response(r) > > but this is error prone, verbose etc etc. > > The right thing is that within a test suite, the get()/post() etc to do the > checks for me - and so it should be possible to create a testclient that > does this, and be able to use this testclient in various test suites. > No, you're mixing concerns. > Is there a simple, clean way to solve this that I'm missing? > class MyTestCase(TestCase): def check_response(self, response): self.assertContains(response, ) def checked_get(self, *args, **kw): r = self.client.get(*args, **kw) self.check_response(r) return r class SpecificTest(MyTestCase): def test(): r = self.checked_get(...) ... That's how I would do it. Shai. -- You received this message because you are subscribed to the Google Groups "Django developers" group. To post to this group, send email to django-developers@googlegroups.com. To unsubscribe from this group, send email to django-developers+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.
Re: Minor feature: Make TestCase pass the testcase instance to self.client's constructor
On Wednesday, January 2, 2013 11:58:04 PM UTC, Russell Keith-Magee wrote: > > On Thu, Jan 3, 2013 at 1:56 AM, Malcolm Box> wrote: > >> Hi, >> >> When creating self.client in TestCase, it would be very useful if the >> testcase instance was passed to the client. >> >> I'm using a replacement client class that does various validation checks, >> so wants to use assert* functions on TestCase, thus takes a testcase >> instance as a parameter at creation time. However this means it can't be >> used as the client_class attribute on TestCase, as this is instantiated >> with no parameters ( >> https://github.com/django/django/blob/master/django/test/testcases.py#L475 >> ) >> >> There are work-arounds, but it feels to me like a reasonably generic >> requirement that a test client may want to refer to the test case it's >> being used with. I think the change can be made largely backwardly >> compatible by changing to: >> >> self.client = self.client_class(testcase=self) >> >> which would only break an existing replacement client class that had an >> __init__ method without **kwargs. >> >> I'm happy to code this up, but wanted to check for any objections first. >> > > Personally, I'm suspicious of any "A owns B, but B knows about A" > relationships. There are certainly occasions when they're required, but > whenever they pop up, it's generally an indication of a set of interfaces > that are closely coupled. > > In this case, I'm not sure I see why this coupling is required. A test > case is a test case. A test client is a test client. Their concerns are > completely separate (evidenced by the fact that you can have a test case > without any client usage, and vice versa). I very much see the test client > as a utility tool for the test case -- really not much more than a > convenient factory for requests -- not an integral part of a test case. > > I don't disagree - and this feature wouldn't require any test client to care about the testcase. Your feature request seems to be stem entirely from the fact that you want > to invoke assertions in the test client code itself -- something that > you're doing because you've got a bunch of extensions in your test client. > I'll leave it up to you to determine if this is the right approach for your > own project, but I'm not convinced it's a general requirement that warrants > binding the test client to the test case. > The general pattern I want to implement is have a test client that makes assertions about all the requests made during a set of tests. For example, it could check that every get() returned cache headers, or that content_type is always specified in responses. Or that the response has certain HTML, uses certain templates etc - ie all of the assertion testing goodness in django.test.TestCase. My concrete use case is a JSONClient that adds a json_get / json_post methods which makes sure to setup content_type etc on the call, and then validates that what came back was valid JSON. The simple, wrong way is to do: def check_response(self, response): self.assertContains(response, ) def test(): r = self.client.get(...) self.check_response(r) but this is error prone, verbose etc etc. The right thing is that within a test suite, the get()/post() etc to do the checks for me - and so it should be possible to create a testclient that does this, and be able to use this testclient in various test suites. Is there a simple, clean way to solve this that I'm missing? Malcolm -- You received this message because you are subscribed to the Google Groups "Django developers" group. To view this discussion on the web visit https://groups.google.com/d/msg/django-developers/-/LNtkPS3EcdsJ. To post to this group, send email to django-developers@googlegroups.com. To unsubscribe from this group, send email to django-developers+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.
Re: Minor feature: Make TestCase pass the testcase instance to self.client's constructor
On Thu, Jan 3, 2013 at 1:56 AM, Malcolm Boxwrote: > Hi, > > When creating self.client in TestCase, it would be very useful if the > testcase instance was passed to the client. > > I'm using a replacement client class that does various validation checks, > so wants to use assert* functions on TestCase, thus takes a testcase > instance as a parameter at creation time. However this means it can't be > used as the client_class attribute on TestCase, as this is instantiated > with no parameters ( > https://github.com/django/django/blob/master/django/test/testcases.py#L475 > ) > > There are work-arounds, but it feels to me like a reasonably generic > requirement that a test client may want to refer to the test case it's > being used with. I think the change can be made largely backwardly > compatible by changing to: > > self.client = self.client_class(testcase=self) > > which would only break an existing replacement client class that had an > __init__ method without **kwargs. > > I'm happy to code this up, but wanted to check for any objections first. > Personally, I'm suspicious of any "A owns B, but B knows about A" relationships. There are certainly occasions when they're required, but whenever they pop up, it's generally an indication of a set of interfaces that are closely coupled. In this case, I'm not sure I see why this coupling is required. A test case is a test case. A test client is a test client. Their concerns are completely separate (evidenced by the fact that you can have a test case without any client usage, and vice versa). I very much see the test client as a utility tool for the test case -- really not much more than a convenient factory for requests -- not an integral part of a test case. Your feature request seems to be stem entirely from the fact that you want to invoke assertions in the test client code itself -- something that you're doing because you've got a bunch of extensions in your test client. I'll leave it up to you to determine if this is the right approach for your own project, but I'm not convinced it's a general requirement that warrants binding the test client to the test case. Yours, Russ Magee %-) -- You received this message because you are subscribed to the Google Groups "Django developers" group. To post to this group, send email to django-developers@googlegroups.com. To unsubscribe from this group, send email to django-developers+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.