From Habari Project
Unit testing the 'Posts' class
This section attempts to perform some initial analysis of the available data about an existing class in an attempt to create some unit tests.
Firstly, we will review the available documentation to analyse the 'Posts' class.
It is possible we will merely duplicate the existing unit tests but we will intentionally defer the review of the existing unit tests until the end in case it prejudices the analysis.
The current specification for the 'Posts' class is virtually a stub. There is no description in English of what this class actually does. However, from reading the sparse technical specification, we can start to make notes about possible unit tests for the 'Posts' class. This isn't a criticism, merely a statement of fact. Habari is a collaborative project. I could have written a comprehensive specification for Posts but I didn't.
Retrieving get($paramarray = array()) by_status($status) by_slug($slug = '')
It appears that we can fetch all posts with a given status although we don't know what the valid range of values for the 'Status' field is (yet).
Having used Habari we can make an informed guess; the list of valid 'Status' values is likely to include 'Published', 'Draft' (saved but not yet published) and 'Scheduled' (to be published in the future).
In a brand new, empty test environment (which is what the all unit tests assume and require), we can test for the following scenarios.
Retrieving all Posts with status 'Published' returns nothing. In this case nothing probably means NULL or an array with zero elements. Obviously, we will need to check which case applies.
We don't know how to create a post entry yet but if and when we do publish a post of type 'Post', then fetching all posts with Status of 'Published' should fetch the newly created post.
All the various attributes (title, slug, body, author, publication date, last updated, tags) must contain the values supplied when we created the entry.
Some post attributes may default to sensible values (user, publication date, status) so the tests should include testing these conditions too. It may be difficult to check that the post is published with the current timestamp.
Does Habari include a status of 'Deleted' for posts that appear to be deleted to the end user but, in reality, are soft deleted in case they subsequently need to be restored from the bin ? In any case, the definitive list of all possible 'Status' values should be specified somewhere.
An interesting question is what happens if we try to fetch posts of a non-existent status like 'Top Secret'. Will the function return NULL ? Or an empty array ? How is this scenario distinguished from a valid post type that has no entries ? Will an exception be raised ?
This demonstrates one of the benefits of unit testing; occasionally the specification of tests raises interesting technical questions about the design or specification. Maybe no-one has ever considered this test case because everyone knows that the only post types are just 'Post' and 'Page'. In any case Habari doesn't even support adding a new post type so why bother coding for this non-existent test case. It would just bloat the code.
Similarly, the same set of tests used for Posts can also be created for entries of type 'Page'. Note that this duplication mean appear redundant as we might assume that the same code is responsible for implementing Posts and Pages and the post type is all that differs. However, that may not always be true. In the future, the whole method of storing and fetching 'Posts' may change as typically, there are more pages than pages. If we write comprehensive unit tests for Posts now, we can easily check, if the Pages handling code is subsequently separated and optimized (refactored), that the new, improved code works the same with exactly the same results as the initial implementation.
Note that the whole issue of whether refactored code is indeed any more performant and efficient than previously is outside of the scope of unit testing. Unit testing is solely concerned with inputs and outputs.
Also, we can use our knowledge of Habari to identify a possible oversight in the Posts specification. Habari supports two different types of posts. 'Post' for conventional blog entries and 'Page' which is intended for static pages (e.g. an 'About' page).
The 'Posts' class doesn't appear to include methods for fetching posts of a certain 'Type'. This seems odd as it seems likely that theme developers would often want to fetch all posts of type 'Post' to be displayed in reverse chronological order for a blog while Pages would be retrieved and typically displayed in a header or navigation bar.
Of course, it is possible that theme developers would use 'Get' to retrieve all published content and filter on the post type but this seems rather inefficient. Maybe a 'Get by_type($type)' would be a useful method to add to the Posts class.
Counting count_total($status) count_all() count_by_author($user_id = '', $status count_by_tag($tag = '', $status)
The 'Count' methods are great candidates for unit testing as they are simple and return an integer.
In a brand new clean environment, all counts should return zero.
After the user 'Michael' successfully publishes a single post tagged with 'Habari', the methods count_total('Published'), count_all() and count_by_author('Michael') and 'count_by_tag('Habari') should all return 1.
If this single post is then deleted (although we don't know how to delete posts yet), the counts should all revert to 0.
An interesting test would be to add a very large number of posts to determine what the maximum number of published posts in a Habari database is.
The specification contains a fairly comprehensive list of attributes (properties) assocated with a Post object.
Again, enumerating all the possible attributes that are associated with a post may be difficult so it may be useful to examine the Habari database to identify the table that stores posts and compare the list of columns on the database table with the number of attributes exposed by the 'Posts' class.
The Habari API documentation is automatically generated daily by 'doxygen'. This is excellent practice as it means the API documentation always represents the current state of the PHP source code. If the description of a function is modified in the source code for the 'Posts' class, then the API documentation automatically gets updated.
[I'd like to put a hyperlink to the Posts API documentation but I can't find out how as it uses an old technology called Frames.]
Again, although the documentation includes some English words, the documentation is technical, very dry and developer centric. For example, it is non trivial for a non-technical user to parse this sentence.
Post::Ascend - Returns the ascending post, relative to this post, according to params The params by which to work out what is the ascending post
Unsurprisingly, we see a number of methods in the API (source code) that are not documented in the 'Specification'. Again, this is not a criticism - unless some automation or a dedicated team of technical authors are working full-time on a project, similar discrepancies could be found on most open source and commercial software projects.
static Post::list_active_post_types ($refresh = false) Parameters: bool whether to force a refresh of the cached values
This (list_active_post_types) is an interesting function. It provides the mechanism to list the valid post types. However, the output of this particular test (by definition) will have to include the currently defined list of post types. Every time, a new post type is added, this test will break. However, that is probably a good thing.
The 'cache' parameter is also interesting. By default, this function appears to ignore cached values so if a post type has recently been deleted without the cache being refreshed, the function can potentially return
misleading incorrect results. Wrong answers fast or right answers slow. Still - at least you have a parameter to configure the behaviour.
Post types are essentially meta-data (data about data) and defining additional post types (e.g. 'Video') is likely to be a relatively infrequent operation by an administrator which makes the inclusion of the cache mechanism more puzzling.
This cache mechanism makes testing this very simple function slightly harder as we now have to understand this additional functionality It would be much better, from a testing perspective, if the cache mechanism was entirely transparent and this function always returned the right answer even if it took 0.27 milliseconds longer.
Posts::count_all() - return a count for the number of posts last queried Returns: int the number of posts of specified type ( published or draft )
Interesting. If the inline documentation is to be believed count_all() is misnamed because it really returns a count of the last set of posts queried. So if you last searched for both of the 'Scheduled Posts' written by 'Fred', it appears that this function returns '2' even if there are a total of 753 posts.This function would be better named 'count_current' or 'count_last_queried'.
The count_total() function (with FILTER parameter = FALSE) counts all posts although the description of the return value references the original two post types ('Published', 'Draft') which is now almost certainly outdated.
Specification of Retrieving Posts
http://wiki.habariproject.org/en/Dev:Retrieving_Posts is an excellent specification (in English) of how retrieving posts works.