Thursday, April 30, 2015

4/29/15 and 4/30/15 - Restructuring Tests

I hit a brick wall and spent about 30 minutes trying to figure out why my before() hook wasn't calling before my BookStorageManager tests (I was getting undefined variables). It turns out that I had forgotten to surround my test code in an it() statement, and so the test code was being evaluated at the wrong time, before the before() method had been called. This was really hard to debug because I was convinced that the problem was with the before statement itself, and I didn't think to check whether the code in the test was set up properly.

Also, I think I was wrong (again) in one of my blog posts.  It seems like calling mock.verify() on a mock doesn't actually clear expectations, so if you have multiple expectations dealing with the same mocked method, you will run into problems.  So I think that doing

beforeEach(function () { mock = sinon.mock(obj); });
is necessary when expecting multiple calls to the same method across different tests.

I also realized that the BOOK_OBJECT constant we use in our tests (for example) wouldn't reflect any new changes we made to how Book objects are generated from librivox json (for example, changing the id from a string to an integer had to be manually done in BOOK_OBJECT alongside app.js). I fixed this somewhat by setting book object to var BOOK_OBJECT = new Book({json: WEB_RESP.book_json}); so that our test book object will be generated from our json each time the tests are run. I'm still a bit fuzzy on this aspect of testing, so this might not be best practice. I'll check with Kevin.

I worked on cleaning up our tests. I moved the tests for each object into separate files. I also moved all the simulated web response objects into their own object so that the global namespace didn't get start getting polluted (even though it was only three things, I like it better this way). So now the responses can be gotten with WEB_RESP.audio_blob WEB_RESP.book_json WEB_RESP.book_xml.

Tuesday, April 28, 2015

4/28/15 - Strings and Integers!

I think I finally figured out the strange test failings we've been getting on our tests when using sinon to expect method calls with arguments. We would get failure messages like:
Unexpected call: test(1234)
            Expected test(1234) once (never called)
on our tests, where the first line is what was actually called, and the second line is what was expected.  They appear identical.

Here's some tests that illustrate what the problem is:
Can you spot the difference? It turns out that the id field in the book object is actually a string, and not an integer!  Obviously the error output is just converting all the arguments to strings before outputting, and so there's no way to tell what type they are just by looking at the output.  To fix this, I'll add a test to the Book object to ensure that it converts strings passed in to integers and update the relevant lines in app.js.

It really annoys me that such a silly thing took up so much of my time though.

Monday, April 27, 2015

4/27/15 - Sinon Deep Equals Problems

In Javascript, {} == {} and {} === {} both evaluate to false. This is because objects in javascript must be compared via deep equals, which iterates through all the keys in the object and checks their equality recursively. So expect({}).equal({}) (regular equal) would cause a test failure, expect({}).eql({}) (deep equal) would pass the test.

In sinon, we've been using the method mock.expects("method").once().withExactArgs(ARG_OBJECT), which compares an object passed into a method with the object we expect it to be (in this case, an object called ARG_OBJECT), which worked here. Now that I think about this, I'm not actually sure why this works. When Finn tried the same thing for our BookDownloadManager tests, the #withExactArgs would fail even though the object being passed in to the expect and the object being recieved by the mock were exactly the same. There is no way (that I can see, at least) to get an exact args deep equals, so we're basically out of luck. Tomorrow we'll try to look into more solutions.

Actually, I looked into it further, and it turns out I jumped to conclusions a bit too fast. I was right to wonder why mock.expects("method").once().withExactArgs(ARG_OBJECT); works when the code we were working with today doesn't, because as it turns out, deep equals is not the problem. As far as I can tell, #withExactArgs does use deep equals.

The problem was actually more deceptive: We use mock.verify() at the end of each test to show that the methods are being called correctly. I assumed that .verify() would clear the expectations so that each test would start fresh, but that doesn't seem to be the case. Instead, our expectations from the first test kept carrying over to the next, causing failure. (Although as I say that, I'm looking here, where we did have multiple .verify()s that worked in succession, so I might be wrong again).

To fix this problem, I'm recreating the mock object after each test, which will start fresh with no expectations:
beforeEach(function () {
    storageMock = sinon.mock(storage); // must re-mock for each test
});
I have no idea if this is what you're supposed to do, because none of the examples I could find used multiple verify statements, but it does work. Luckily, creating the mock isn't a very processor intensive operation, and we don't have many tests (yet), so this doesn't cause any noticeable performance decrease.

Here's the test at this point in time. I also moved the helper objects into their own file, which is automatically loaded with tests.js thanks to this line in karma.conf.js. Splitting tests.js up into smaller test files will just be a cut and paste operation!

Sunday, April 26, 2015

4/26/15 - Mocking with Sinon

When I wrote the Testing with Sinon post a couple days ago I meant to talk about how I was able to use mocking.

Given a test like this: (there's a lot of code here, but you can definitely get through it—read the comments)
Sinon's mocking is interesting, because it keeps the behavior of the object being mocked intact while still allowing you to test how the methods have been called. In the case of this test, I don't care about what the BookDownloadManager#downloadBook method is actually doing, so I created a dummy object with stubbed methods (line 8), and used sinon to mock that (line 9).

At first, I was confused about whether to use the original dlManager object or the dlManagerMock I had created with sinon, but I found that the mock you get from sinon.mock() is an object for you as the tester to verify that methods are being called, while the original object passed into .mock() continues to be what you actually use to interact with the objects being tested. Sam pointed out that this was done to preserve the namespace of the original object. For example, what if dlManager had a function called #verify()?

Saturday, April 25, 2015

4/25/15 - More Unit Tests

Today, I let Finn drive and he started writing the BookDownloadManager tests, although we didn't get to a point where the tests actually do anything.

I also noticed that somewhere along the way I had broken the BookDownloadManager in our app, even though it passed all the tests. This was because I had forgotten to pass an HttpRequestHandler into the object in app.js on this line that I was passing in correctly in the tests. Mr. Elkner suggested standardizing the way the objects are generated between the app and the tests. Another solution to this problem would be to throw an exception if something vital is missing from the arguments object. Unfortunately, neither var b = vital_arg || throw Error or var b = vital_arg ? vital_arg : throw Error work, so we would need an if statement for each variable we want to ensure is defined.

I emailed Kevin and linked him to yesterday's blog post, where I had complained about how long this it statement was. He said that I was right in being suspicious about the length, and that the test is testing more than one thing. He suggested splitting this test into two, one to test chapter, and one to test book. In hindsight, this is really obvious and I should have figured it out on my own. Also, it made me realize that the wording on the test is wrong. It says that the BookPlayerPageGenerator actually 'generates download book and download chapter buttons', which isn't true; it only adds onclick events to the existing elements.

Thursday, April 23, 2015

4/23/15 - Testing with Sinon

Today I started testing the BookPlayerPageGenerator, and wanted to test that the BookDownloadManager was getting the proper messages. User interface is really hard to test, and one aspect of what I ended up with really bothered me.
I really dislike how long the description inside the it() is. I vaguely recall that when I was starting out in rspec, I learned you don't want to go over a certain character limit with your test description, otherwise it suggests that something is wrong (line 27) with the objects you are working with.  If you are interested, the unabridged test code is here and the object being tested is here.

Also, the test file is getting pretty long (225 lines), and there's a lot of sample constructs defined at the top of the file (like the BOOK_OBJECT and CHAPTER_OBJECT variables shown in this gist) that should be moved into their own file, and maybe even encapsulated in some sort of globals object.  Each object's test should also be moved into its own test file.

Wednesday, April 22, 2015

4/22/15 - LibriFox Audio Downloading

Today, Finn and I worked on setting up a way to download the audiobook mp3s. It was hard because there is very sparse documentation for the Firefox OS file system API. For example, on this page, only one example of a "selector" for enumerate is given. We wanted to find all the files in the librifox/ directory, and eventually Finn guessed that you could do #enumerate('path/'), which turned out to work. There also isn't really a good way to access the filesystem short of using their API, so when we ran into trouble we weren't sure if our problem was in the file write code or the code we were inputting to read the filenames back to see if they were written.  There is no native file browser in Firefox OS.

Two new objects were added to app.js, a BookDownloadManager, which handles the downloading of chapter and book audio files, and a BookPlayerPageGenerator, which sets up the book.html user interface.

One thing I don't like about the code in this stage is how much object generation is done just in the body of app.js. For example, BookPlayerPageGenerator needs to know about three elements on the page, and right now we are creating the instance and setting up the selectors right in the body of the file, which adds clutter. App.js is getting really big, so we really need to look into splitting it into smaller files. I'm not sure how this works with jQuery Mobile though.

Here's a picture that shows today's progress.

You can see on the console there (under the not well formed errors, which are the most vague and unhelpful error messages ever), that the downloaded chapter gets written to 'librifox/book-id/chapter-index.mp3'.

jQuery Mobile's built in sliders were terrible to use as progress bars because they are designed to be interactive, and we don't want the user sliding them around while a file is downloading.  I ended up making my own progress bar that's just a div nested inside another div, which requires way less setup code than what we would need to do for jQuery mobile sliders.

None of this code has tests written yet, so that's something we will work on tomorrow.

One thing I was unsure of when writing the 'generator' objects is how to handle the args hash being undefined.  Right now I have the line var args = args || {};, which sets the args to an empty object if it's excluded. I'm thinking that it might be better not to do that at all, because the object won't function correctly without those arguments, and it's probably better to fail explicitly right at the point it tries to set variables from the args, rather than later on when the code tries to use them and finds them to be undefined.

Tuesday, April 21, 2015

4/21/15 - Gitignore

I haven't blogged in a while because I was preparing for the UMD programming contest last week, but today Finn and I worked on refactoring the book page of LibriFox.

I also wanted to blog in order to write this down in case I needed to do it again:
I put all my Learn C the Hard Way exercises into a git repository so that I would have access to their revision history, but I forgot about all the compiled files and ended up with this as the repo folder.  I don't have make set up to use the .out suffix, so I couldn't just add the compiled files to the gitignore.  Instead I found this guide online and set up my .gitignore following it:
File: .gitignore

# ignore everything
*
# Don't ignore directories, so we can recurse into them
!*/
# Don't ignore other important files
!.gitignore
!*.c
!*.java
!*.py
!*.rb
Then, to remove all the files I didn't want that had already been committed, I did git rm -rf . --cached, which removed all the files from my git path, while keeping them on the local machine. Then I did git add . to add all the files allowed by the gitignore back into the path.
This isn't the perfect solution, because every time I add a new file type I'll have to add it manually, but it works for now. I ended up with a much cleaner directory.

Also I found a javascript web mocking library called Nock, so hopefully that will allow us to test the HttpRequestHandler object easily.

Wednesday, April 8, 2015

4/8/15 - File Name Too Long

Today I kept working on the SearchResultsPageGenerator, but I'm having trouble getting it to work because I'm getting this error: NS_ERROR_FILE_NAME_TOO_LONG.  Apparently this is a problem with windows, and I couldn't find any info on how to fix it.  I tried it with one of the flame devices, and that didn't work either (same error). I tried a different Firefox OS app I had, and it worked fine, so this problem is specific to the librifox app.

Tuesday, April 7, 2015

4/7/15 - Windows Shell


Since I use windows on my laptop, I've had to use git through cmd.exe, which is very feature-lacking. It doesn't have tab support and copying/pasting is buried in the menus with no keyboard shortcut. I followed this tutorial in order to get Console2 running with git. I installed Console2 to my Program Files (x86) directory, and figured out that when you change your settings through the menu, they will silently revert back unless Console2 is running in administrator mode, since it doesn't have permission to write to the program files directory otherwise.

I also started refactoring the book search results page into a BookSearchPageGenerator just like I did with ChapterListPageGenerator. I also started writing tests for it, but I'm not sure the best method for removing the http dependencies. Right now I'm making a mock of the http parser, and adding the methods. It's also tough to figure out how much is really necessary to test for, since UI is pretty complex. For example, is it important to test that the appended elements are <li>s? I don't think it is.

4/4/15 - UMD Contest Problems

Today I did two 2013 UMD contest problems: 3 - Guessing Game I and 4 - Tree Isomorphism. Guessing Game I was really easy, and I had already done Tree Isomorphism with Sam on Wednesday and Thursday, but I decided to do it again because Sam did the typing (and most of the thinking) on the last one, and I remembered our approach to the problem well enough to re-solve it. I've noticed that a lot of times I'll read one of these UMD problems and just have no idea what to do, and before talking through it with Sam, Tree Isomorphism was definitely one of those cases where I just had no clue how to approach it.  After seeing the solution once, though, I was able to come up with it again on my own.  I didn't keep track of time, but it probably took about an hour and a half, which isn't very good considering I had already seen the solution once.

One thing about our approach to this problem that confused me and slowed me down was the List<List<TreeNode>> type returned by #permutations(). I couldn't remember our previous solution exactly, so I had to play around with the recursion to get the method to work correctly with the return type. Another thing that threw me off was the choice between List<TreeNode> children and rootNode.children in method arguments. I found that it made the most sense to do the former, because a root node might not always be present, such as when the #permutations() List<List<TreeNode>> was in use. For example, I originally had #treeEquals() take two root nodes and compare children (rather than taking two lists of nodes), but I realized that this would require me to add additional root nodes when all I had was a list of children, such as when checking the permutations of one tree against the other.

I also looked at 5 - Guessing Game II, and it's the same problem—I don't have enough experience to be able to come up with a strategy. I did work through their solution, and I think understand it pretty well, although I wouldn't have been able to come up with that on my own.

Monday, April 6, 2015

4/6/15 - Permutations in Ruby

I rewrote my ruby permutations algorithm to use yield instead of return. This is a lazy algorithm: rather than generating all permutations at once, it generates them as they are needed. So the line permutations(%w[a b c d]).first(5) only generates the first 5 permutations of the array. Here is the implementation: As it turns out, this function is already built into ruby! This statement does the exact same thing.

Sunday, April 5, 2015

4/5/15 ChaptersListPageGenerator code and tests

Repo diff
ChaptersListPageGenerator code
ChaptersListPageGenerator tests

For some reason, there are two ways to test equality in our tests, assert.equal() and expect(). I think the former is the mocha expression, and the latter is the chai expression. I tried to convert all our tests over to the expect() syntax. I also found this page, which was helpful for writing the expect statements.

In testing the chapters list page generator, I made my own stub for HttpRequestHandler, which is used to fetch the RSS chapter data that populates the chapters list. There's a stubbing/mocking library we might want to look into as well.

I wasn't quite sure how to test the output of #generatePage since it's all within the page elements, and this is what I ended up with. I also found that I had to explicitly instantiate the <ul> targeted by #pageGenerate as a jQuery Mobile listview, otherwise this line in app.js would cause an error saying that the listview had not been initialized.

I added a property to karma.conf.js to allow console.log statements to be outputted to the server console, which is helpful for debugging the tests

I don't think we need to test the private methods of ChapterListPageGenerator; they aren't part of any public interface, so there is no risk of cascading changes causing problems beyond #generatePage. if something is changed.

I looked at how to make public and private methods in an object, and how inheritance works in JavaScript:

With a Foo() object set as Bar's prototype, all new Bar objects to send anything that they don't know how to handle up the message chain to Foo. So a method defined in Bar would override an identical method in Foo, but a method defined in Foo and not in Bar will work as though it is being called on Foo directly. This source was helpful in figuring stuff out.