If you’ve done any sort of Ruby On Rails tutorials or reading, you often run into the concept of testing. Testing is a big deal. Whether you enjoy Test Driven Development (TDD) or Behavior Driven Development (BDD, for which the name and abbreviation is currently the extent of my knowledge on the subject), you must know how to test. Testing is vital, not only in making sure the code you build works, but, it turns out, it’s vital if you ever want prospective employers to take your projects seriously. At a recent Meetup at Invoca about the concept of contributing to open source projects, I was speaking to a couple people who work there, one of whom was in management. I brought up what I call my nba project (you can view the repository here ) if you want to, and mentioned that because of how it functions I hadn’t really written any tests for my models even though I had written (for me) some complicated code to go out out to the web, get raw data (as JSON) and process it into a variety of models (4 at this time, but more may come in the future if I can figure out inexpensive database hosting). I was basically told by said manager that when looking at a prospective employee’s code, they look at the testing first. Thus, if I ever wanna be presentable (or employable), I’m going to have to learn how to write tests for everything I write on my own.

You can do what are called integration tests, feature tests (which they are called in my Rails 4 in action book but might be the same as integration tests), controller tests, model tests. You can probably test anything really (I presume there is route testing out there, I’ve just never run into it in any of my tutorials). In my experience though, most tutorials really function on the integration and/or feature testing, but you are going to build models and controllers and if you’re going to build anything of significance, you’re going to write some code unique to your project and it’s all going to have to be tested properly, and sometimes that code runs in the back and can’t be tested using the standards taught in the tutorials.

Thus, that’s where I was at. At first, I thought I could get away with just using shoulda-matchers, and I built some pretty simple basic ‘should’ statements, but I knew that wasn’t enough (and later on will prove to break because after some period of time I forgot how my code was functioning). My nba project has a lot of background stuff going on in the models, that starts with an inputted date accessing one method in the Game model which then does quite a bit across multiple models to download all the game data I currently use for whatever the inputted date was. I had built a working version that I ‘tested’ by inputting dates manually a few months ago. However, that’s not what testing really is. I had never delved in to testing to see if the data what was populating my various models was actually the correct data. When I thought about it, it seemed daunting, because I’m going out to the web and getting information, looping through it and then getting more information from the web based on the original loop and then possibly even more trips depending on what data already exists in a specific table. Writing tests for that seemed terrible daunting and, well, beyond me, and I told those folk at Inovca that that was exactly how I felt.

Well, being the nice folk that so many experienced programmers seem to be to us lesser learned folks, they didn’t just laugh at me, they explained to me the concept of mocking your calls to the web. They told me RSpec couldn’t really mock properly, and whether it can or can not is not germane to this article except that the idea of mocking to be able to have the data I needed to test my models was enough to spur me into action.

Note: As you’ll read through this article, I did successfully test what I was trying to test, but I’m not sure I was mocking the data as it is traditionally defined, but I am able to get through the processes without having to make calls to the web while still having the data that would be returned.

And what’s the first action when you are learning anything new? That’s right, to the internet. After some serious searching using various inquiries into my friendly search engine, I cam upon a presentation that was last altered in 2012, entitled Testing Remote Services with WebMock and VCR.

Aha! I thought to myself as I skimmed this presentation, this seems exactly like what I need, but before immediately installing WebMock I went to the Ruby-Toolbox (if you work in Ruby on Rails you know it, if not, you don’t need to) and found that WebMock belonged to a group called Mocking Web Requests, and, after some (very very) quick research, WebMock seemed the right place to start with.

So, I put WebMock in my gem file, did our favorite bin/bundle and let it do it’s thing. I was totally ready to go forward with some serious model testing, and I was psyched, and that should have been my warning. But, I’m spoiling the surprise. Let’s take thing in the order they came, shall we?

Day 1 - March 12th, 2016 - 6 Hours

My first step actually turned out to be learning about cURL, because I had never really used it myself before. I only had used it in copy/paste from tutorials to install stuff.

According to wikipedia, cURL is a computer software project providing a library and command-line tool for transferring data using various protocols. Basically, for those that don’t know, this means you can send various types of web (and possibly other) requests via the command line and receive the request response directly. For instance, type curl www.google.com on your command line would return (to your screen) the code making up the google home page. (Yes I did try this.)

That’s not very useful in and of itself now is it? If you want to be able to access downloaded data for your test requests, you need to be able to store them so that you can call to them later no, don’t you? Fortunately, there are flags (various options if you will) you can provide to the curl command. One of these will allow you to direct the output to a file instead of just seeing it on your screen. The -o flag will allow you to write the output to a file. This entry on the command line, curl -o path/to/file www.website.com/page, will output the content of the target page in the target file.

But John, you say to yourself (because if you said it out loud people would look at you funny), I read that presentation in your first link and they used a different command set up, curl -is in their example. You, observant reader, are correct. So, since it would bug me if I didn’t, I had to do a bit more research to figure out the -is flag combination and how it would differ from the -o flag provided on the wikipedia page. A quick swagbucks search (because, well, if you search the web a lot, why not earn even small rewards for it?) led to documentation page of what seems to be a type of curl homepage. If you scroll through this page quickly, you’ll see quite a few different flags and their descriptions.

Fortunately, the flag description is alphabetical (sort of, it’s alphabetical by the – option, not the - option, but close enough), so it was easy to find the two flags I was looking for.

  • -i tells the curl request to include the HTTP header in the output, whereas -o (for output) does not.
  • -s turns off the progress bar. (If you use any sort of flag to create a file with your cURL request, a progress bar will show up on your terminal. I don’t think this matters one way or another personally, but use it if you want)

Obviously, there are a lot more flag options listed, but my goal was just to find out what the -is flag was all about. I knew now what it was and that I could easily stick with -o (or -os if I cared about message bars) to get the JSON request files I needed for testing.

At some point, you might have thought to yourself, John, there are already a lot of sites out there, written by people who are better coders and better statisticians than you, so why would you do this? You’d probably be smart to ask that question and my response would be that this was the driving project that first got me started down my path of learning some sort of serious web development (see the first article, the first step), so even if it’s only ends up demonstration site for prospective employers/clients in the future, it’d be nice to get it workable someday. At least that’s how my mind rationalizes it to me, so let’s just leave it at that, ok?

So to try and test my methods that process the retrieved data without actually testing the data retrieval itself (perhaps I should since I’m doing this myself and not using an API? That’s another thought for another day), I needed to use the cURL command three separate times:

  1. Get the games for the day
  2. Get the player stats for a specific game
  3. Get the specific information for a single player from 2

So, since I kind of went about this backwards, it should have been simple. I had already built the 7 models needed and the various scripts to download and process the data to populate the 4 relevant tables of game, team and player data. But, as often happens, things weren’t always that simple. You see, after I had successfully built the code to download and populate the data (and do a few other dynamic calculations for daily fantasy sports), I had tried to work on some Rails code to write complex queries so I could present useful data. Unfortunately, that proved kind of frustrating to me so I had left this project alone for a couple months. In that time, the NBA.com website people altered their setups, so my code to get the Game information (1 above) no longer worked. Getting the statistics and player information (2 & 3 above) still worked, but without the game information the project is slightly less useful. So, what’s an intrepid intermediate beginner to do? Well, he could have given up, but as we know he’s done that before and he should stop doing that. So to the web he went. I won’t bore you with the details, mostly because I didn’t track them that carefully (which can happen when I’m trying to solve an issue, web search, find suggestion, try, fail, repeat until problem solved), and it could get very boring to read about all my various web queries and failed attempts, but after 3 hours, and finally happening upon the curb gem, I was able to once again download and get the JSON for the games for a given day, so I could return to my originally scheduled ‘mock’ testing.

The test date I chose was December 25, 2011, because I thought it would be one of the old christmas games, but that was the strike shortened season so there were five games, which is fine I guess. Testing should work with any quantity of games, right? (I realize as I’m editing this on Easter Sunday 2016, that it also is a tool that could show how far back I could go with my data, because once it works, I would want to get as much as I can right? You can never have too much data I bet statisticians say, or would if they heard me say it.)

For completeness of description and itemization, I created a folder within app/spec/models to store my JSON files that would be used by webmock once we got there. I exeucted the curl -o command on the 3 links necessary to get the test data and for completeness tested that they could be parsed parsed via the JSON gem using irb.

By the way, this is the last mention you’ll hear of webmock going forward. It turns out that while it got me started on the path, it wasn’t the right tool for the project. Using JSON.parse(File.read(path/to/downloaded/json/file)) was what I needed to run my model testing, because, after all, that’s the first step in the process of my methods, parsing the JSON.

Day 2 - March 14th, 2016, 3 Hours, after work.

So, now I could test, and I figured, well I solved my problem regarding getting the game information already so all my processing of the JSON downloaded should work right? Oh come on, you know the answer to that. I wrote a simple test to determine that when games were added the Game database total would increase it’s total count by the appropriate number of games, but alas, it failed quickly and I was told my code was trying to work on the dreaded nilClass. And after reviewing the error message, and the code it pointed to, I was reminded that Rails no longer stores seed data in the test database without adding a specific line of code into your rails_helper.rb file. (I learned this while doing testing on this blog actually, and while there are lots of great sources for help out there, even with StackOverflow it took me a couple hours to ask the right question to learn this piece of information). So into the rails_helper.rb file I add Rails.application.load_seed right under require rspec/rails. Now I don’t know what the regular amount of seed data is, but my seed file currently seeds the Divisions and the Teams (36 total records between the two) each time. Someday if I launch it, the seed data might also allow for seeding of game data, but that would be up to the user I think.

So, now I had to figure out how to properly write Rspec code so that when I run my method, the model(s) that records are added to can be counted (i.e the Game model should increase by 5 and the Participant model should increase by 10 because each game has 2 participants.) Yes, my method adds to two different models in the same functionality, which might be some dirty code.

Actually, this first method adds data to *three models. After the game and participants are entered, all the games entered in this execution are looped through to get the player statistics for both sides. I couldn’t figure out how to ‘mock’ this just yet, so I just commented out the code that was getting in the way of my more simple tests. If I had done this as standard TDD, (write test, fail, write code until test passes, refactor, also known as red-green-refactor), the code would have been written one step at a time, but as I was doing this backwards, I had to comment out code and pretend it didn’t exist yet.

When I ran this test, it seemed to enter the games, but not the participants, because another nilClass error was thrown. I commented out the nillClass error code and tweaked my expect code thanks to a Stack Overflow response and was able to test that my adding of games to the Game table was working. So that’s good, first test done, and I have tested that my add_games method is working. However, the next step in the method, adding to the participant method is not working. I tested to make sure that the Participant.count changed by twice the numbers of games and sadly it told me it was zero. It turns out this was my own fault. I first started playing with shoulda-matchers and put some validations on the Participant model that won’t work because the data has to be added in a bit of a loop. You can’t just pull a winner and a loser from the data. You must compare for each team in a given game and see which has more points. I removed the offending shoulda-matchers and again the tests passed.

Alas, after I refactored these simple (yet complicated for me for the first time) tests I had been working through just to check that the right amount of records were being added to my two models, I had been at this close to three hours, after a full day of work. So I did the git add-commit-push samba and put this ‘to bed’ until the next day.

Day 3 - March 15th, 2016, 2 Hours during Meetup office hours.

The first step that I did was to clean up the location of my working tests. The successful participant test from Day 2 was moved from game_spec.rb to participant_spec.rb with the same basic setup. This did work with the commented out code from Day 2, but when I uncommented the code, the last line blew everything to pieces because it calls a method in another model that scrapes other JSON and you can’t do that through RSpec. Plus the call loops through all the games added so it’s time consuming as well. I ran through numerous searches and options on StackOverflow and could find nothing, then a meetup mentor mentioned something to me which led me to test a few more options in figuring out how to stub code until I successfully was able to stub out the method with the following code Player.stub(:get_playerstats).and_return(true). This line in both tests made them pass without commenting out any code. RSpec told me that I was working with some deprecated code and I should probably deal with it, but that’s a future issue. Right now the tests are passing, and they’re just the first tests I have to write, so we take the pass and move on. A little GACP (I’ve decided that I’m creating a new abbreviation that represents git add, git commit, and git push) and on to the next test.

Day 4 - March 17th, 2016 3 1/2 Hours between work Lunch Break and after work.

My first tests were very basic. I was just really testing if the method that is the ‘first step’ in my process created the right amount of records in my Game model and my Participant model. I have not yet checked whether or not the right data has been added, so that’s the next step. So now I have to write more expect statements that represent what the data should be inserted.

The first thing I did was visit the nba.com results page for the night in question and note the various resultant data that I would need to test to make sure my Participant model had been populated properly.

Next up is making sure that the right data for the participants got included. I decided to do this in multiple steps, first testing some of my scoped methods to see if they gave the proper results (in terms of the count of records) and then I’d get specific on each game to verify that the right information for each game had been assigned, and, because I tend to over do it, I figured it doesn’t hurt to check to make sure the details for each game are right.

Step One - Testing the scopes

First thing I did was some slight refactoring on my participant_spec.rb file to make all the tested scenarios fall under a context. Using before do allowed me to write the code to stub out that pesky method that was causing troubles earlier and also to add all the participant data before each scenario (as recommended) without having to rewrite those two lines of code as part of each scenario. Let’s hear it for DRYing out your code folks. At the moment, the scopes I have created are pretty simplistic, they search for all winners, losers, home teams, away teams, based on a simple flag in two distinct database columns (winloss, homeaway if you’re interested). The first step was to make sure that there were the right (equal) amounts of home / away teams and win / loss teams. This worked out pretty well, and then I decided to test the to see if chaining my scopes together gave the right record count. As I was preparing to test all four combinations my brain clicked that since the scopes are each ‘paired opposites’, I only needed to test one complete pair with only one of the other pair to verify I was getting the right counts. Thus I verified that the home team won twice and the away team won thrice on Christmas Day, 2011.

Testing the specifics.

That is all well and good that my scopes are giving me the right record count, but I need to make sure that my records reflect that the Knicks beat the Celtics at home 106-104, and 4 other known outcomes, to make sure that all my data populated properly.

So - my first thought was to populate a hash using the known outcomes and my seeded data (teams and conferences). After a few minutes of sketching, I came up with how the hash should look in general.

test_hash = {"nbacomid_of_game" => {home_team: ["team abbreviation", "Win / Lose Identifier", Points scored as an integer], away_team:[repeat the same array]}, repeat as necessary }

After playing around with my test JSON in irb to get the nbacomid’s of all the games and referring to my notes from the earlier tests, I was able to quickly build the hashes.

NBA.com uses leading zeroes for their unique identifiers of their games. I’ve had some previous bad experience ignoring leading zeroes, so, in my Game model, the identifier is a string field, hence the use of the string key with the => ‘rocket identifier’.

So, I headed back to my trusty web search tool and ended up at StackOverflow (you can’t possibly be surprised anymore) because when you write Game.first.participants.home to isolate the home team information of the first game you return an ActveRecord_AssociationRelation, which was currently beyond ‘intermediate beginning’ understanding of ActiveRecord. So I needed to know how to isolate the data that I specifically wanted to test from my test_hash back above. This time I got the answer that I needed on the first try. However, you have to go down to the third answer (as of March 17, 2016) where they mention pluck.

Pluck is something I’ve read about before, but never could think of where I would use it practically, but now I could. I could use pluck on the AssociationRelation to get the specific attribute I was looking for and compare it to the proper index of the from the arrays that are contained in my hash above. (Getting the team abbreviation took an extra step, and I’m just going to put the code for it here right now because I’m not sure i can explain it properly Team.find(test_game.participants.away.pluck("team_id"))[0][:abbreviation]. So if the away team were the NBA team that I root for, that code would return “PHI”.

Since I was working with pluck for the first time, a trick I discovered was that pluck returns an array, even if it’s only one value in the array. Thus when I wrote the second half of my expectation, i placed the value i was accessing from an array between the [] of an array as that was the fastest way to get my tests to pass, and pass they did.

And that’s it, for now, I think. As you may remember, I had three JSON files I downloaded in the beginning (way back up there) to run my model testing on, and I will, but not in this article, or today (or tomorrow for that matter). I’m working on multiple projects of my own (remember, this blog is actually a project of mine) and also contributing to a project from my local Meetup (up above, where I got help at my office hours, you gotta give back folks)

So if you made it this far, I hope for two things really:

  1. I hope it made sense to you in terms of the English language
  2. I hope that those of you who know more than me about RSpec, model testing, etc…don’t find the way I went about this TOO backwards, and please if you have any suggestions on how to do this better in the future (or at least where to look) please don’t hesitate to tell me in the comments section.

By the way, everything is of course published in commits at the github repository above if you want to delve into more detail of what I wrote. Feel free.