Classical unit test as Groovy Spock method - unit-testing

I have a Groovy Spock method that has the following pattern:
def "My test"() {
def a = createA();
assert a.fieldLevel1.isValid()
def b = a.fieldLevel1
assert b.fieldLevel2.isValid()
def c = b.fieldLevel2
assert c.fieldLevel3.isValid()
...
}
As you can see it's hard to break it on blocks as assertions and variables definitions are mixed together. What would be the "spock" approach to write such kind of test?
UPDATE:
The test has the following structure because c.fieldLevel3.isValid() is actually c.fieldLevel3 instanceof A, so I can't proceed if the field is invalid.

The "classical" way of unit testing is keeping tests unitary. That is, testing one thing per test, which seems not to be the case in this example.
That being said, however, you could group all the assertions in an expect block after all the setup code in a setup block:
def "My test"() {
setup:
def b = createA().fieldLevel1
def c = b.fieldLevel2
def d = c.fieldLevel3
expect:
b.valid
c.valid
d.valid
}
Notice that i've shortened the assertions by using Groovy's goodies to access isValid() as valid and calling that method on the auxiliary objects directly.
Also, i haven't used the usual when/then Spock blocks because this test case doesn't seems to align very well with being a stimuli/response on a given system. But you could also use many when and then alternated blocks if you so desire:
def "My test"() {
when: def b = createA().fieldLevel1
then: b.valid
when: def c = b.fieldLevel2
then: c.valid
when: def d = c.fieldLevel3
then: d.valid
}

Not sure why you did not accept the answer above it looks quite good.
As a minor difference, you could also do:
def "My test of creating an A"() {
when:
def a = createA()
then:
a.fieldLevel1.isValid()
a.fieldLevel1.fieldLevel2.isValid()
a.fieldLevel1.fieldLevel2.fieldLevel3.isValid()
}
Whether or not you 'like' this depends upon how closely you follow Demeter's 'Law' - Groovy seems to make that less relevant than in the past.
If the complexity of the actual underlying objects is such that this is not an effective approach at validating them, then they might deserve their own Unit Tests.

Related

Django Model function behavior changes based on how many tests being run

I have a need for a uniqueID within my Django code. I wrote a simple model like this
class UniqueIDGenerator(models.Model):
nextID = models.PositiveIntegerField(blank=False)
#classmethod
def getNextID(self):
if(self.objects.filter(id=1).exists()):
idValue = self.objects.get(id=1).nextID
idValue += 1
self.objects.filter(id=1).update(nextID=idValue)
return idValue
tempObj = self(nextID=1)
tempObj.save()
return tempObj.nextID
Then I wrote a unit test like this:
class ModelWorking(TestCase):
def setUp(self):
return None
def test_IDGenerator(self):
returnValue = UniqueIDGenerator.getNextID()
self.assertEqual(returnValue, 1)
returnValue = UniqueIDGenerator.getNextID()
self.assertEqual(returnValue, 2)
return None
When I run this test by itself, it runs fine. No issues.
When I run this test as a suite, which includes a bunch of other unit tests as well (which include calls to getNextID() as well), this test fails. The getNextID() always returns 1. Why would that be happening?
I figured it out.
Django runs each test in a transaction to provide isolation. Doc link.
Since my other tests make a call to getNextID(), the first row gets deleted after the first test that makes such a call is complete. Subsequent tests never find (id=1), due to which all subsequent calls return the value 1.
Even though I don't think I would face that situation in production, I went I ahead and changed my code to use .first() instead of (id=1). Like this
def getNextID(self):
firstRow = self.objects.first()
if(firstRow):
That way I believe it would better handle any future scenario when the database table might be emptied.

Using mock to test if directory exists or not

I have been exploring mock and pytest for a few days now.
I have the following method:
def func():
if not os.path.isdir('/tmp/folder'):
os.makedirs('/tmp/folder')
In order to unit test it, I have decided to patch os.path.isdir and os.makedirs, as shown:
#patch('os.path.isdir')
#patch('os.makedirs')
def test_func(patch_makedirs, patch_isdir):
patch_isdir.return_value = False
assert patch_makedirs.called == True
The assertion fails, irrespective of the return value from patch_isdir. Can someone please help me figure out where I am going wrong?
Can't say for sure having the complete code, but I have the feeling it's related to where you're patching.
You should patch the os module that was imported by the module under test.
So, if you have it like this:
mymodule.py:
def func():
if not os.path.isdir('/tmp/folder'):
os.makedirs('/tmp/folder')
you should make your _test_mymodule.py_ like this:
#patch('mymodule.os')
def test_func(self, os_mock):
os_mock.path.isdir.return_value = False
assert os_mock.makedirs.called
Note that this specific test is not that useful, since it's essentially testing if the module os works -- and you can probably assume that is well tested. ;)
Your tests would probably be better if focused on your application logic (maybe, the code that calls func?).
You are missing the call to func().
#patch('os.path.isdir')
#patch('os.makedirs')
def test_func(patch_makedirs, patch_isdir):
patch_isdir.return_value = False
yourmodule.func()
assert patch_makedirs.called == True

Skipping a test based on the pass of a previous test

I'm using the django test suite with nose and trying to figure out how I can run a followup test if a previous test fails. Is that possible?
Here is how I'd do it:
Have a global variable called test_condition_a = False shared among test cases.
Try/catch assert in test_a and catch the exception when it fails so that you can set test_condition_a = True before the test exits.
Use #unittest.skipIf(test_condition_a) on all other test cases that you want run only if test_a fails.
EDIT On second thought, the above is not going to work as the test order is random. Your best bet would be to do something like this
class Test(TestCase):
def setUp(self):
...
#unittest.skip("Skip by default")
def testB(self):
#test code
def testA(self):
try:
#test code
return True
except Error:
return False
def testA_then_B(self):
if (self.testA()):
self.testB()

Unmocking a mocked object in Django unit tests

I have several TestCase classes in my django application. On some of them, I mock out a function which calls external resources by decorating the class with #mock.patch, which works great. One TestCase in my test suite, let's call it B(), depends on that external resource so I don't want it mocked out and I don't add the decorator. It looks something like this:
#mock.patch("myapp.external_resource_function", new=mock.MagicMock)
class A(TestCase):
# tests here
class B(TestBase):
# tests here which depend on external_resource_function
When I test B independently, things work as expected. However, when I run both tests together, A runs first but the function is still mocked out in B. How can I unmock that call? I've tried reloading the module, but it didn't help.
Patch has start and stop methods. Based on what I can see from the code you have provided, I would remove the decorator and use the setUp and tearDown methods found in the link in your classes.
class A(TestCase):
def setUp(self):
self.patcher1 = patch('myapp.external_resource_function', new=mock.MagicMock)
self.MockClass1 = self.patcher1.start()
def tearDown(self):
self.patcher1.stop()
def test_something(self):
...
>>> A('test_something').run()
Great answer. With regard to Ethereal's question, patch objects are pretty flexible in their use.
Here's one way to approach tests that require different patches. You could still use setUp and tearDown, but not to do the patch.start/stop bit.
You start() the patches in each test and you use a finally clause to make sure they get stopped().
Patches also support Context Manager stuff so that's another option, not shown here.
class A(TestCase):
patcher1 = patch('myapp.external_resource_function', new=mock.MagicMock)
patcher2 = patch('myapp.something_else', new=mock.MagicMock)
def test_something(self):
li_patcher = [self.patcher1]
for patcher in li_patcher:
patcher.start()
try:
pass
finally:
for patcher in li_patcher:
patcher.stop()
def test_something_else(self):
li_patcher = [self.patcher1, self.patcher2]
for patcher in li_patcher:
patcher.start()
try:
pass
finally:
for patcher in li_patcher:
patcher.stop()

Can I use "Where" block and helper methods at the same time in spock

I have a specification which essentially looks like this:
def "my example specification"(){
given:"some mocked object which depends on a object created in the where clause"
def display = mockDisplay()
and:"a activityt"
def activity = new ConfigActivity(display)
when:
activity.doStuff()
then:
1 * display.select()
where:
dependency << new Dependency()
}
private mockDisplay() {
def display = Mock(ConfigActivity.Display)
display.addDependency(dependency)
return display
}
I understand that that the "mockDisplay()" method is out of scope from the "where" clause. However since a few specifications tend to get rather cluttered with boilerplate code (not in this over simplified example offcourse) I really need some way to reuse my "given" statements and in doing so I would really like to make use of the Spock "where" clauses as well.
Is this doable? Or is there another way to go about this problem?
There is no magic way to access a data variable from a helper method, but you can pass it as a method parameter.