How to test a function with N distinct paths? - unit-testing

I have a function which validates a model class where it has to check every member of the class to be non-null or non-empty in case of a String.
The logic for this is_complete function is something like this:
def is_complete(profile):
if profile.first_name in (None, ''):
return False
elif profile.last_name in (None, ''):
return False
elif profile.dob is None:
return False
.
.
.# and checks so on for all members of the profile instance
.
.
return True
My question since the number of possible paths the execution can take is quite large and increases in proportion to the number of member variables of profile to be checked, how does one reliably write tests for all the possible paths?
Right now, I have two simple test cases:
where only some of the members are set and checks assertFalse(is_complete(foo))
where all the members are set and checks assertTrue(is_complete(foo))
But I have a feeling that this may not be enough.

I'm not sure what you mean by having MxN paths. In the posted code you have as many paths as fields + 1.
Create a helper method that creates a profile that is complete and passes is_complete.
Add a test method to verify is_complete(profile) is True for the complete profile.
Add one test method for each field, with the following steps:
Call the helper method to create a complete profile that we know would pass is_complete
Break the field under test
Verify that is_complete returns False
You will have as many test methods as fields + 1.
Btw, instead of this:
if profile.first_name in (None, ''):
You can writer simpler:
if not profile.first_name:

You can test this with randomization:
Write a function that creates a random Profile object with all the fields filled (could be random, but valid).
Randomly change one or more fields to either None or empty
Pass the object to the is_complete() function.
I can show this only in Java with Qala Datagen. I assume you're interested in the negative paths, so it can look like this:
#Test void profileIsNotComplete_ifAtLeastOneFieldIsBlank() {
Profile p = Profile.random();
callOneOrMore(
() -> profile.setName(nullOrEmpty()),
() -> profile.setLastName(nullOrEmpty()),
...);
assertFalse(profile.isComplete();
}
Note, that this code actually tests more - it also checks the combination of fields set to null/empty. If in Python there are no such libs, you can write some utility methods for yourself.
NB: only one path is going to be tested per one execution. You can run it many times (a thousand?) to make sure that all the paths pass. Then in CI you can run it only once if this is not a mission-critical functionality and you're not afraid that it's going to break often.
Otherwise if you really want this to be covered 100% for every invocation, you can violate some of the good practices and just combine all these tests in one (otherwise there are going to be too many tests which complicate reading them):
#Test void profileIsNotComplete_ifOneFieldIsBlank() {
assertFalse(Profile.random().setName(null).isComplete());
assertFalse(Profile.random().setName("").isComplete());
assertFalse(Profile.random().setLastName(null).isComplete());
assertFalse(Profile.random().setLastName("").isComplete());
...
}
This doesn't test combinations though, but you can combine both approaches. The positive case is very easy in both approaches:
#Test void profileIsComplete_ifAllFieldsAreFilled() {
assertTrue(Profile.random());
}
More info on randomized testing is here.

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.

In Django, how can I calculate or update certain model fields BEFORE any validation happens?

So I'm new to Django...
First some background on how we do things now. We have a custom php system but I am constructing an improved inventory management system in django using only the admin interface. We store part numbers, and it is essential that we do not store duplicates. Part numbers can sometimes be entered with hypens, periods, spaces, etc. We need to be sure that duplicate parts are not added no matter what kind of formatting is entered. With our existing non-django system, we use a regex to strip anything from the string that is not a-zA-Z0-9. The actual entered part number is persisted, and the cleaned number is persisted to the db as well. Then when someone is adding a new part or even searching for a part, this cleaned version of the part number helps to avoid this ambiguity. We do the same for the manufacturer name.
My way of emulating this in django was to add the part_number_clean field along with the part_number field to the model. Then I overrode the save method to calculate the clean part number like so (manufacturer as well):
def save(self, *args, **kwargs):
self.manufacturer_clean = re.sub(r'[^a-zA-Z0-9]', '', self.manufacturer).lower()
self.part_number_clean = re.sub(r'[^a-zA-Z0-9]', '', self.part_number).lower()
super(CatalogProduct, self).save(*args, **kwargs)
The problem is, I need to unique on a combination of part number and manufacturer:
class Meta:
unique_together = ('part_number_clean ', 'manufacturer_clean ')
When I try to save a duplicate record, I get a database integrity violation. So it seems like django is evaluating the unique fields before the save function is called (which makes sense). I just need to know how or which method I should override to calculate these fields BEFORE any validation.
Additionally, I am interested in adding a third field to the unique_together mix that may or may not be filled out. If it is not filled it will just have an empty default value. I hope this will not cause any issues.
It would also be great if when the user tabbed-out of the manufacturer and part number fields, and both were not empty, some js would see if that product exists already, and offer the user the option to click a button and be whisked away to that record, before they waste their time filling out the rest of the data only to find that it already exists. I'm guessing this lies way outside the realm of the admin interface without serious hacking. Is there any way to somehow integrate this with the admin interface? Its working great for me up till now...
I figured it out. I'm posting the answer for anyone else that is curious. This was actually very simple in the end to implement in the model. All one needs to do is implement (override?) the clean() method of the model. In the method, I calculate and set my special fields, then be sure to call self.validate_unique() after. Works like a charm! No need to raise any exceptions, the form will display the error on top perfectly. Doing this in the save method will not work, as the exception cannot be thrown by your code or django at that point. Here is the code:
class CatalogProduct(models.Model):
manufacturer = models.CharField(max_length=100)
manufacturer_clean = models.CharField('Manufacturer',max_length=100,blank=True,editable=False)
part_number = models.CharField(max_length=100)
part_number_clean = models.CharField('Part number',max_length=100,blank=True,editable=False)
def clean(self):
# Calculate manufacturer_clean and part_number_clean
self.manufacturer_clean = re.sub(r'[^a-zA-Z0-9]', '', self.manufacturer).lower()
self.part_number_clean = re.sub(r'[^a-zA-Z0-9]', '', self.part_number).lower()
self.validate_unique()
The model is only responsible for describing data and how that data should be represented between your Python and database environment. It's because of this atomic role that models don't care about validation and what you've just went in there and introduced it.
You need a model form. It can clean the manufacturer and part number and also ensure that uniqueness constraints are satisfied as part of the validation process.

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.

DRY in django queries, reverse queries, and predicates

I am frustrated that in Django I often end up having to write methods on a custom Manager:
class EntryManager(Manager):
def filter_beatle(self, beatle):
return self.filter(headline__contains=beatle)
... and repeat pretty much the same method in a different Manager for a reverse query:
class BlogManager(Manager):
def filter_beatle(self, beatle):
return self.filter(entry__headline__contains=beatle)
... and a predicate on Entry:
def headline_contains(self, beatle):
return self.headline.find(beatle) != -1
[Note that the predicate on Entry will work on Entry objects that haven't even been saved yet.]
This feels like a violation of DRY. Is there some way to express this once and use it in all three places?
What I would like to be able to do is write something like:
q = Q(headline__contains="Lennon")
lennon_entries = Entry.objects.filter(q)
lennon_blogs = Blog.objects.filter(q.reverse(Entry))
is_lennon = entry.would_filter(q)
... where 'headline__contains="Lennon"' expresses exactly once what it means to be 'an Entry about "Lennon"', and this can be used to construct reverse queries and a predicate.
The best place for this is a custom manager. According to django's guidelines a manager class is the best place for code that is affecting more than one object of a class.
class EntryManager(models.Manager):
def filter_lennons(self):
return self.get_query_set().filter(headline__contains='Lennon')
class Entry(models.Model):
headline = models.CharField(max_length=100)
objects = EntryManager()
lennons = Entry.objects.filter_lennons()
You should never rarely have to do the following:
if entry.headline.find('Lennon') >= 0:
because the filter should take care of restricting the result set to the instances you're interested in.
If you're going to be using the same filter multiple times, you can create a custom manager or a simple class method.
class Entry(models.Model):
...
# this really should be on a custom manager, but this was quicker to demonstrate
#classmethod
def find_headlines(cls, text):
return cls.objects.filter(headline__contains=text)
entries = Entry.find_headlines('Lennon')
But really, the DRYness has already been contained within the Queryset API. How often are you really going to be hard coding the string 'Lennon' into a query? Usually, the search parameter will be passed into a view from a GET or POST. Perfectly DRY.
So, what is the actual problem? Other than exploring the queryset API, have you ever had to hard code lookup values in multiple queries like your question?
For the "reverse filter" case you can use a subquery:
Blog.objects.filter(entries__in=Entry.objects.filter_beatle("Lennon"))
Reusing or generating predicates is not possible (in general) as there are predicates that cannot be expressed as queries and queries that cannot be expressed as predicates without db access.
My most common use for the predicate seems to be in asserts. Often something like:
class Thing(Model):
class QuerySet(query.QuerySet):
def need_to_be_whacked():
# ... code ...
def needs_to_be_whacked(self):
return Thing.objects.need_to_be_whacked().filter(id=self.id).exists()
def whack(self):
assert self.needs_to_be_whacked()
for thing in Thing.objects.need_to_be_whacked():
thing.whack()
I want to make sure that no other code is calling whack() in state where it doesn't need to be whacked. It costs a database hit, but it works.

How to test Models in Django with Foreign Keys

I want to make sure I am testing Models/Objects in isolation and not as one huge system.
If I have an Order object and it has Foreign Keys to Customers, Payments, OrderItems, etc. and I want to test Order functionality, I need to create fixtures for all of that related data, or create it in code. I think what I really need to be doing is mocking out the other items, but I don't see an easy (or possible) solution for that if I am doing queries on these Foreign Keys.
The common solutions (fixtures) don't really let me test one Object at a time. I am sure this is partly caused by my app being way over coupled.
I am trying my darndest to adopt TDD as my main method of working, but the way things work with Django, it seems you can either run very trivial unit tests, or these massive integration tests.
[Edit] Better explicit example and a some more humility
What I mean is that I seem to be able to only run trivial unit tests. I have seen people with very well tested and granular modules. I am certain some of this can be followed back to poor design.
Example:
I have a model call Upsell which is linked to a Product model. Then I have a Choices model which are children of Upsell (do you want what's behind door #1, #2, #3).
The Upsell model has several methods on it that derive items necessary to render the template from their choices. The most important one is that it creates a URL for each choice. It does this through some string mangling etc. If I wanted to test the Upsell.get_urls() method, I want to have it not depend on the values of choices in the fixtures, and I want to not have it depend on the value of Product in the fixtures.
Right now I populate the db in the setUp method for the tests, and that works well with the way Django backs out the transaction every time, but only outside of setUp and tearDown. This works fairly well except some of the Models are fairly complex to set up, while I actually only need to get one attribute for it.
I can't give you an example of that, since I can't accomplish it, but here is the type of thing I am doing now. Basically I input an entire order, create the A/B experiment it was attached to, etc. And that's not counting Product, Categories, etc. all set up by fixtures. It's not the extra work I am concerned as I can't even test one database-based object at a time. The Tests below are important, but they are integration tests. I would like to be building up to something like this by testing each item separately. As you pointed out, maybe I shouldn't have chosen a framework so closely tied to the db. Does any sort of dependency injection exist with something like this? (beyond my testing, but the code itself as well)
class TestMultiSinglePaySwap(TestCase):
fixtures = ['/srv/asm/fixtures/alchemysites.json','/srv/asm/fixtures/catalog.json','/srv/asm/fixtures/checkout_smallset.json','/srv/asm/fixtures/order-test-fixture.json','/srv/asm/fixtures/offers.json']
def setUp(self):
self.o = Order()
self.sp = SiteProfile.objects.get(pk=1)
self.c = Customer.objects.get(pk=1)
signals.post_save.disconnect(order_email_first, sender=Order)
self.o.customer = self.c
p = Payment()
p.cc_number = '4444000011110000'
p.cc_exp_month = '12'
p.cc_type = 'V'
p.cc_exp_year = '2020'
p.cvv2 = '123'
p.save()
self.o.payment = p
self.o.site_profile = self.sp
self.o.save()
self.initial_items = []
self.main_kit = Product.objects.get(pk='MOA1000D6')
self.initial_items.append(self.main_kit)
self.o.add_item('MOA1000D6', 1, False)
self.item1 = Product.objects.get(pk='MOA1041A-6')
self.initial_items.append(self.item1)
self.o.add_item('MOA1041A-6', 1, False)
self.item2 = Product.objects.get(pk='MOA1015-6B')
self.initial_items.append(self.item2)
self.o.add_item('MOA1015-6B', 1, False)
self.item3 = Product.objects.get(pk='STP1001-6E')
self.initial_items.append(self.item3)
self.o.add_item('STP1001-6E', 1, False)
self.swap_item1 = Product.objects.get(pk='MOA1041A-1')
def test_single_pay_swap_wholeorder(self):
o = self.o
swap_all_skus(o)
post_swap_order = Order.objects.get(pk = o.id)
swapped_skus = ['MOA1000D','MOA1041A-1','MOA1015-1B','STP1001-1E']
order_items = post_swap_order.get_all_line_items()
self.assertEqual(order_items.count(), 4)
pr1 = Product()
pr1.sku = 'MOA1000D'
item = OrderItem.objects.get(order = o, sku = 'MOA1000D')
self.assertTrue(item.sku.sku == 'MOA1000D')
pr2 = Product()
pr2.sku = 'MOA1015-1B'
item = OrderItem.objects.get(order = o, sku = 'MOA1015-1B')
self.assertTrue(item.sku.sku == 'MOA1015-1B')
pr1 = Product()
pr1.sku = 'MOA1041A-1'
item = OrderItem.objects.get(order = o, sku = 'MOA1041A-1')
self.assertTrue(item.sku.sku == 'MOA1041A-1')
pr1 = Product()
pr1.sku = 'STP1001-1E'
item = OrderItem.objects.get(order = o, sku = 'STP1001-1E')
self.assertTrue(item.sku.sku == 'STP1001-1E')
Note that I have never actually used a Mock framework though I have tried. So I may also just be fundamentally missing something here.
Look into model mommy. It can automagically create objects with Foreign Keys.
This will probably not answer your question but it may give you food for thought.
In my opinion when you are testing a database backed project or application there is a limit to what you can mock. This is especially so when you are using a framework and an ORM such as the one Django offers. In Django there is no distinction between the business model class and the persistence model class. If you want such a distinction then you'll have to add it yourself.
Unless you are willing to add that additional layer of complexity yourself it becomes tricky to test the business objects alone without having to add fixtures etc. If you must do so you will have to tackle some of the auto magic vodoo done by Django.
If you do choose to grit your teeth and dig in then Michael Foord's Python Mock library will come in quite handy.
I am trying my darndest to adopt TDD as my main method of working, but the way things work with Django, it seems you can either run very trivial unit tests, or these massive integration tests.
I have used Django unit testing mechanism to write non-trivial unit tests. My requirements were doubtless very different from yours. If you can provide more specific details about what you are trying to accomplish then users here would be able to suggest other alternatives.