Welcome to my home page. Here, you will find professional and personal information.
|
||||||
|
Welcome to my home page. Here, you will find professional and personal information. When my brother was young, he begged for a peanut butter and jelly sandwich. Mom was in the middle of something and kept telling him to wait. So, he made his own peanut butter and jelly sandwich. And of course, jelly’s the best part, so he put a lot of jelly on it. It was a grand sandwich, good as any he ever had. Finally, mom came down, and saw that he made his own sandwich, and while he was at it, painted the kitchen in jelly. Oh, no. Mom was angry. When I was the same age, I asked my grandma for a bolagna, lettuce and tomato sandwich. I was 2. She said, “Jon, you’re old enough to make it yourself.” And she handed me a sharp knife, and I don’t remember killing myself. A few years later, I was making my own peanut butter and jelly sandwich. Dad hands me a butter knife, and says, “Here, use this.” A knife is for the peanut butter, and spoon is for the jelly. You can’t use a butter knife for jelly. You’ll never get enough. So I get a spoon. Uh oh! Big trouble. He’s pissed, and no sandwich for me. I’m And a few years later… If you made Mike a peanut butter and jelly sandwich… you were his favorite brother. My family in a sandwich. So, here’s the problem. I’m tasked with working on a large legacy code base, no tests exist, and I have a need to convince my team that it’s easy to test (so that I’m not swimming upstream, and writing 1x tests for 10x code.) Test::More is easy! But, it seems that just writing a bunch of procedural test code would turn into a big mess in short order. I wouldn’t write customer code this way, because I wouldn’t want to support it. If we go down this path with the tests, they’ll soon become an unmanageable mess. (We need to test 10k’s of lines of code). We need good code, if this is going to scale. So, I thumb through “Perl Testing: A Developer’s Notebook (with Ian Langworth)” and this on how to organize tests. After following the guidance from this article, you can now break down your tests into blocks, which reside in test scripts. Also, you write a testing class that helps organize code for TEST::More. You can do things like automatically run set_up and tear_down routines on each test, or group tests and write group headers. Cool.
TEST: { # foo can't be demoralised
throws_ok ( sub {
$foo->set_bar( LOW );
} ,
qr/Should not let others lower the bar/,
'Should not allow lowering of bar');
ok( $foo->is_high_achiever , 'Should still be a high achiever' );
}
But this organization still seems lacking. The TEST block, and anonymous subs used this way just ain’t right. I can never extend or reuse that sub, or at least, it’s use makes that opportunity limited. Also, It’d be nice if a my test structure emulated my code structure. By setting up a strong convention, writing tests will seem easier. I’m looking for something like this:
package Test::Myapp::Register::Validate;
use Test::More;
sub test_require
{
requires_ok( 'MyApp::Register::Validate' );
}
sub test_new
{
my $v = MyApp::Register::Validate->new();
isa_ok( $validate_object, 'MyApp::Register::Validate );
$v = undef;
$v = MyApp::Register::Validate->new( connect => 1 );
isa_ok( $v, 'MyApp::Register::Validate' );
}
Now, that looks like some best practice code. But this module is living in a lib somewhere in ‘Test/MyApp/Register’, and my tests are living in a .t file. It’d be nice if I could so something like this in my .t file: use Test::MyApp::Register::Validate; So, let’s add a run_tests function to our class. Something like this:
sub run_tests
{
my $self = shift();
my @tests = (
'test_require' => 'Test Require',
'test_new' => 'Test the constructor'
);
$self->SUPER::run_tests( @tests );
}
We’ll leave the implementation details of run_tests implementation up to the superclass for now. That seems reasonable to me. Now, I can have 1 testing package for every application package in a similar file structure. ie, MyApp::Register::Validate is tested by Test::MyApp::Register::Validate Also, there’s a close relationship (convention) between the new() function in the app class and the test_new() function in the test class. That seems easy! and scalable! So… now, I can write a script to parse my applications library, and figure out which test packages I need. Remember, there’s a 1:1 relationship. Also, I can do a quick parse job for “subs+(.*)”, and I can stub out a test function for every application function. Maybe, something like this as a place holder for all the functions I haven’t written tests for:
sub test_confirm_registration {
TODO:{
todo_skip( "test_confirm_registration: No test written yet", 1 )
}
}
Now, we have a goal! 100% function coverage. Write at least 1 test for each function. Not 100% code coverage… but at least we can run a basic test for, did I get a value back when I expected a value? Easy! Organized! Scalable! Reasonable! (After hitting 100% function coverage, we can march on to 100% code coverage!) Also, notice, instead of hard coding in a set_up / tear_down function, I can setup all kinds of helpers in my test classes (And they’re reusable! yah!) So, something like
sub create_object
{
return MyApp::Registration::Validate->new();
}
sub destroy_object
{
my $self = shift();
my $o = shift();
while( $o->connected )
{
$o->disconnect();
wait 10;
}
$o->DESTROY();
}
sub test_validate_email
{
my $self = shift();
my $o = $self->create_object();
..
$self->destroy_object( $o );
}
Now, you’re not pinned down to just 2 helper routines. Make as many as you want. Call them from within your subs. Re-use them if you want. Extend them, override them. Re-use! Okay, my test class would look something like this (Cause I haven’t won the Moose argument yet).
package MyApp::Test;
use constant LOG_COMMENT_CHARACTER => '#';
sub new
{
my $class = shift();
my $self = {};
bless( $self, $class );
return $self;
}
sub run_tests
{
my $self = shift();
# notice, I use an array even though my data structure looks like a hash
# I need to maintain the order of the tests... although, if the client
# wants to use a hash, that's fine
my @tests = @_;
while( @tests > 0 )
{
my $function = shift( @tests );
my $description = shift( @tests );
$self->test( $function, $description );
}
}
sub test
{
my $self = shift();
my $function = shift();
my $description = shift();
print "n" . LOG_COMMENT_CHARACTER ."-----" . $description . "n";
eval{ $self->$function() };
if( $@ )
{
warn "There was a fatal error while running the test $description";
}
}
1;
Notice we’re using the $self->$function() notation here instead of passing an anonymous reference. This way, I can extend my test class, and add or overide functionality (reuse), and I can call other object methods from within my test group. And, I no longer care how run_tests or test is implemented. I can focus on writing short test functions, nothing else. That’s easy! This produces output similar to that article. Except, we’ve added the stubs in that will print out something like: not ok 18 #TODO #SKIP – test_confirm_registration: test written yet Okay, that’s it. I’m sure I reinvented a wheel (Test::Harness?). But I wanted to share my approach and thinking, even if it’s not complete yet. What do you think? First Draft… A component hierarchy is used in the configuration management of complex physical systems (ie, tanks, plains, and automobiles). By modeling a complex system in a hierarchy, certain configuration management algorithms become easy. For example, determining the impact of a change to a component is limited to all the parents and children of that component, wherever it exists in the hierarchy. Also, creating a parts list is just a report of all the children of the component, or all the children + n levels. This is a way for an expert to capture expert knowledge and allow non-experts to query it later using pre-defined algorithms. This method of configuration management on physical systems has been in place for over 50 years. (Military standard for Configuration Management. More resources later.) https://acc.dau.mil/GetAttachment.aspx?id=142238&pname=file&aid=27622&lang=en-US Also, many information technology shops organize their alarms in hierarchies. This allows you to suppress whole branches of alarms, for example, during maintenance. These alarm hierarchies were lacking. By suppressing large trees of data, valid messages wont’ get fired. Also, there’s no way to tell the state of a system by the state of it’s subcomponents. For example, consider the following common configuration application->server->cpu. The operations center gets an alarm that the CPU is high. That says nothing about the state of the application. The application might be humming along just fine. Also, assume this configuration: company->application->server->cpu. Some of these hierarchy tools tried to roll up a simple red, yellow, green state to their parents. In these cases, company was always red. There’s always an alarm going off somewhere. Finally, rarely were these alarm hierarchies very good at configuration management functionality; although, the data seems quit similar. One solution is to add state to the hierarchies. By adding a state calculation to every element in the hierarchy, you can make the determination that: a) yes, we have high cpu on a server b) the application is running just fine because there are no errors being generated or requests being denied or taking longer than prescribed. Once you have this logic, you can start doing a lot more than just having an operator monitoring the system. You can do smart stuff like, the CPU is high, page the server folks, but not the application people. Another common case is that the network is unavailable. A lot of times, alarms start firing off for the servers because they are unavailable. Knowing that the network is unavailable, the system can be intelligent and automatically suppress server pages. So, by adding state to an alarming hierarchy, you are starting to get some of the export value available in a configuration management hierarchy. If experts were able to draft the correct model, you could do more than just page the right folks when something went wrong. You could actually train the system to fix it. So, imagine a hierarchy of objects that have state. State is determined by the state of objects children and / or information from outside the system. When the state of an object changes, an action is taken. How many other problems could this be applied to? Think about: But why stop there? I found this thesis on a similar subject. The author describes an entire system based on state hierarchies as a programming strategy that overcomes some of the limits of object oriented programming. http://msdl.cs.mcgill.ca/people/tfeng/thesis/node1.html Next time… An example Perl API / screen mockups of a “State Hierarchy Engine”. In Perl, there is more than 1 way to do it, and usually, that’s what gives you the option to make it pretty. It’s like creative writing. In Java, you’re stuck with the way that the language designers prefer, and it may not be that great for the particular code you’re writing. But your development team has to get it. The goal is easy to maintain code. You can’t stop on the first iteration of code that meets the requirement. You’re not done yet if it’s still ugly. I spend a lot of time squashing bugs on a 10 year old code set. On the first pass, my average solution is 20-30 lines of code. I usually check in 5-10 lines by the time I’m done. That’s less risk of change to the system, and 1/2 to 1/3 as much code to support in the future. And it’s pretty. (okay, this is just my reply to robonperl , but I want to make sure my feed to ironman is working.) Here are ten things I look for in an employer. How does your employer score? Also, would you add anything else, or dispute any of these as being important benefits? 1) Casual or no dress code |
||||||
|
Copyright © 2010 Hogue.org - All Rights Reserved |
||||||
Recent Comments