Thursday, March 1, 2012

A Primer on Unit Testing

Seneca offers a course on introductory Java Programming for fourth semester students of its CPA, CPD, and BSD programmes. I'm enrolled in it right now, and I must say, I feel like the course's name sells it short to some extent. Students learn many things beyond the scope of the Java API. For instance, in our first assignment, we were asked to work in groups to craft and perform unit tests on the classes we designed using the JUnit framework. Automated testing is a fairly new concept to me, but I found it to be useful and interesting. It would seem that the idea is to speed up the development process by testing code quickly and efficiently rather than writing and rewriting test mains that may overlook certain aspects of code and are really tailored to one type of logic or a particular naming convention. As with any other team-based exercise, it has its ups and downs, and I wanted to share some things I feel are good practices in test-driven development.

Ensure that all business requirements are understood before you start designing and writing code

This is something that seems like common sense for most developers, but we're also human, and we have a penchant for missing things that ought to be obvious (missing semicolons, anyone?). The great thing about working in a group is that you can sort out ambiguities and point out things that you might have missed. You can talk over what the client seems to want, debate as to whether a particular design meets those requirements, and optimize the design to suit your needs.

 For each business requirement, device a use case and desired output

A big part of testing is understanding exactly what the end user experience is going to be like. What will the user do? And what do they expect to see as a result? Think about all that is expected by a client from your solution, and what those expectations entail for the end user. This will inform your design and your tests.

Test each business requirement individually, then use a suite to barrage the completed design

It will save you time and effort to test a business requirement in a vacuum to determine if it meets business requirements than to wait until the very end to test everything and having to dive into layer upon layer of code that is likely to feature layered inheritance and multiple imported packages. This is an arduous process and one I had to endure in delivering this assignment.

Choose sensical testing values, especially when testing numerical inputs

It's important that you account for all kinds of user input, yes, but you should ensure that you can parse the input easily and visualise the output. Choosing a string of random numbers will not help a developer test their output in a timely fashion.

 Document your tests appropriately

Commenting and documenting your tests are an essential part of the process. Commenting assembly line code isn't really a necessity - if your code is well-written, it should speak for itself - but testing code is a different story. A developer should be able to trace their exceptions and logical issues easily, and commenting code can give them a great deal of insight into where to look.


I'm sure there's a lot more that can be said about this process - entire sections of bookstores are devoted to it - but this is what I've thought of with my little exposure to this concept. I hope it's of use to anyone who might read this. And if you're more knowledgeable about the topic, please do share your thoughts on it with me!

Wednesday, October 12, 2011

Implementing a Queue

There are many ways to hold a bundle of data in memory. Novice programmers will be aware of the array, a data structure that holds data in sequence within a block of memory. The key feature of an array is the use of indices assigned to each item in the array. This allows us to quickly recall items from memory. We can even dynamically allocate memory to the array using C++'s new and delete keywords.

In programming, just as in economics, there is no such thing as a free lunch. We pay for the speed of an array by losing the ability to change the amount of data held in an array. If we create an array to hold n elements, there is no way to make it hold n+1 elements without allocating new memory. If, for instance, we create an array holding n ints, and we want it to hold just one more int (n+1), the total cost of the operation is 2n+4. That is, if we had 100 ints and we want it to hold 101 ints instead, we would need 804 bytes of memory - 400 for the first array, 400 more for the first 100 intes of the second array, and 4 more for the last int.

If we are willing to sacrifice a bit of speed, we can use linked lists to structure our data instead. Linked lists contain 'nodes' which hold data and pointers to the next node in the list. There are actually many types of linked lists, but for now, let's take a look at the queue list. As a data structure, a queue replicates the behaviour of a queue in real life - like the lineup at a drive through or at an amusement park. Nodes move from the back of the queue to the front, and at the front, they are deleted from a queue.

So how does this look in C++? Let's take a look:

//
//  main.cpp
//  Queue
//
//  Created by Natesh Mayuranathan on 11-10-12.
//

#include <iostream>

using namespace std;

class Queue; 

class QNode {
  int _data;
  QNode* _next;
  
  QNode(int data, QNode* next = (QNode*)0){
  
    _data = data;
    _next = next;
    
  }
  
  friend class Queue;
  
};


class Queue{ 
  
  QNode* _front; 
  QNode* _back;
  QNode* _current;
  
  public:
  
  Queue(){
    _front = (QNode*)0;
    _back = _front;
  }
  
  void add(int data){
    
    //create a new node item and a pointer to it
    QNode* temp = new QNode(data); 
    
    //if the list is empty, add temp to the front of the list
    if(isEmpty()) {
      _front = temp;
    }
    
    //if the list is not empty, point back's next to temp
    if (_back) {
      _back->_next=temp;
    }
    
    //assign temp to back
    _back = temp;
}//add a node to the list 
  
int remove() {
  int val = _front->_data;
  delete _front;
  _front = _front->_next;
  return val;
}//remove node from the list
  
bool isEmpty(){
  return !_front;
}
  
~Queue(){
  while (!isEmpty()) {
    remove();
  }
  
}
  
};

int main (int argc, const char * argv[])
{

  Queue q;
  
  for (int i=100; i<1000; i+=100) {
    q.add(i);
  }
  
  while (!q.isEmpty()) {
    cout << q.remove()<<endl;
  }
  
  return 0;
}



Monday, January 31, 2011

RapidSVN: A Primer for the rest of us

I recall once speaking to a friend in a concert lineup. Both of us being the nerds that we were, we'd been in an excited conversation about Perl. It somehow dawned on another person in line that we were talking about something or other related to computers, and in the interest of conversation asked, "So you guys are in IT, huh?" at which my friend bristled. His reply was simply, "I suppose...but don't ask me to fix your computer."

I often recall this comment in the midst of coding or simply using an application when what I think ought to be a simple task escapes me. It makes me feel frustrated and vulnerable. Most recently, I was setting up and making use of the revision control program, RapidSVN, and ended up having a great deal of trouble creating a directory structure within my branch as well as being able to get what I needed out of the repository. I spoke to Fardad after class (as this happened during the class where he was introducing SVN in detail, as well as the process involved in collaborative coding with SVN), and we arranged the needful.

Afterwards, I found myself with some free time during the afternoon, and decided to boot up RapidSVN and try again. This time I was more successful, and so I decided to create a step-by-step pseudo-transposition of the steps provided on the OOP344 wiki for Mac users who shall likely end up with RapidSVN. It is (at least for me) a bit slow, but it is functional and handy, and with a little practice, one can get used to it as well as SVN in general.

Get RapidSVN (of course)

From the OOP344 wiki, here's a link to the disk image. It's one of the simpler ones that simply includes the executable for you to copy to your Applications folder. Peanuts.

Create a folder to house a working copy of the repo on your home computer.

Another easy step: simply use Finder or Terminal to create a folder that's named logically to which RapidSVN can route a working copy to. Terminal may be preferable, because you can copy the path, which you'll need for the next step

Add the repository to RapidSVN

Ready to sink your teeth into RapidSVN? Launch the app. 



Fairly Spartan interface. I'll go through some of the more relevant (to us) commands, but first click on Bookmarks.



You'll get a list of options. Add Existing Working Copy, Add Existing Repository, Switch Repository URL, Edit Bookmark and Remove Bookmark. We're going to select Add Existing Repository.

Add your SVN account URL. You will have to provide a username and password. Obviously, this can refer to any applicable repository. Check out your bookmarks tab. If the add was successful, your repository is now available to browse! Also, you will no longer have to log in to the repo (from the current workstation). Click on the account in the Bookmarks tab.



The directory structure will be familiar to you, but it's incomplete. Now to make it feel a bit more like home (I guess). The next two steps can be done in any order, but if you perform them as presented, you'll save yourself the trouble of having to merge multiple times.

Create the directory structure Pt. 1 - create your workspace


It's important to be careful of this; it's where I got into a bit of trouble, mostly because of my overzealousness and because, admittedly, I didn't listen as closely as I ought to have. I entered the Modify menu and attempted to create a new directory, for which we don't have permissions. Eep. Here's a tip for you: just don't open the Modify menu at all. I was about to type, "in the root", but I think using the contextual menus is more reliable in almost all cases. Anyway, open up the branches directory.



Right-click anywhere inside the whitespace. Select Make directory A prompt shows up which allows you to name it. Do so, and click OK. The message that appears tells you that your action has resulted in a commit. I provided a comment. I suppose you should, too.




Create the directory structure Pt. 2 - create a task folder
Follow the same instructions as in the previous step to create a folder called task inside your workspace. This is simply a placeholder; you will be renaming it as need be.

Create the directory structure Pt. 3 - branch the trunk into your workspace
Go back to the root and right-click trunk. Select copy, open up your branch, and paste. And that's it, your workspace is complete...on the repository. Your workstation still doesn't have your directory structure.

Merge workstation with repository
Click on the Repository menu and click merge. Enter the path/to/your/working/copy, and click OK. Your workstation now has the same directory structure as the repo.



Follow Steps 4-6 from the Development and Submission steps using SVN entry on the OOP344 wiki



And that's that. Hopefully this reference proves to be handy to coders using revision control on OS X.

Addendum: Useful hints

If the idea of having to create a new working copy is daunting to you, consider storing the one from your working computer on  the always handy Dropbox application. You can be fairly assured that you have the latest version of the code in case you're working on a computer other than your own this way, and it acts as a backup in disaster scenarios.

One other thought might be to use RapidSVN to version control files on your own hard drive if only to practice with the interface and version control example code of your own. This may also be great for rolling back extreme changes to your code. You can accomplish this by using the svnadmin create command as follows:

~ User$ svnadmin create /path/to/repository

And you're set!

Coda

Any error indications, comments or suggestions would be appreciated. I want this to be as accurate as possible. Thanks!

(Postscript: Sorry about the image sizes messing with the layout. Still working out the kinks in the Blogger platform)