![]() |
A problem, I'm definitely connected to the internet. |
Oops. This bug had a few annoying characteristics:
- It was hard to reproduce and had to obvious order to causing the problem
- I couldn't reproduce it locally
- The server stack trace was useless -- because we use an open session in view filter the root cause was a database problem but only showed up at the end of the filter when the transaction is committed
- Restore the database with a set of test data
- Try to reproduce the problem locally
- Record each action taken until the problem occurs
- Repeat until its consistent
As with the previous post, we make heavy use of PageObjects to abstract the details of the markup and what-not from the test case:
package http.voting; import static org.junit.Assert.assertEquals; import http.SeleniumTest; import org.junit.Test; import pages.IndividualRecording.IndividualRecordingPage; import pages.song.SongPage; import pages.voting.RecommendPanel; public class TestUpvote extends SeleniumTest { protected boolean needsDatabaseReset() { return true; } @Test public void reproduceErrorForUserActivityAndDuplicateKeys() { // go to recording 8 IndividualRecordingPage recording8 = new IndividualRecordingPage(driver, 8); driver.get(recording8.getURL()); // upvote it recording8.focus(); RecommendPanel votingPanel = new RecommendPanel(driver); assertEquals(RecommendPanel.VotingResult.SUCCESS, votingPanel.upvote()); // go to song page SongPage song257 = recording8.gotoSong(257); // upvote song twice assertEquals(RecommendPanel.VotingResult.SUCCESS, votingPanel.upvote()); assertEquals(RecommendPanel.VotingResult.ALREADY_VOTED, votingPanel.upvote()); // go to "July 8th, 1978 - Roslyn etc.." song257.gotoAssociatedRecording(26); // upvote (didn't work in bug) assertEquals(RecommendPanel.VotingResult.SUCCESS, votingPanel.upvote()); } }
Minor note: the needsDatabaseReset() method tells the SeleniumTest class to restore the database to a fresh state each time a test is run.
You can see the test case deals with specific actions such as "upvote" and "go to song" instead of searching the website for different elements to click. This is all handled by the different page objects. For example, here is the relevant section of the RecommendPanel class:
public VotingResult upvote() { final int currentVoteCount = getCurrentVoteScore(); getUpvoteButton().click(); try { // success if the votes increase or the "error box" pops up -- although we still need to check it (new WebDriverWait(source, 5)).until(new ExpectedCondition<Boolean>() { public Boolean apply(WebDriver d) { return getCurrentVoteScore() > currentVoteCount || getVisibleErrorBox() != null ; } }); int newVoteScore = getCurrentVoteScore(); if(newVoteScore > currentVoteCount) return VotingResult.SUCCESS; else if(getVisibleErrorBox() != null && getVisibleErrorBox().getText().contains("You already voted")) return VotingResult.ALREADY_VOTED; else return VotingResult.ERROR; } catch(Exception exc) { exc.printStackTrace(); return VotingResult.ERROR; } }
Here, we're simply clicking the "recommend" button and ensuring that the result is either an increase in the current vote score, a popup saying "you already voted," or an error message.
With this test, the problem is now easily reproducible. The actual problem is very unexciting. We log user actions by IP address to decrease the chance of duplicate voting. The table we use is mapped to multiple classes with the id "generator" set to increment. This caused duplicate primary keys to be generated when new entries were added. Changing the generator to "identity" fixed the problem.
In contrast to the previous bug, this one was hard to reproduce but an easy fix. Stay tuned for more bugs that are both irreproducible and hard to fix!*
And for reference, the PageObjects used for this test:
IndividualRecordingPage
SongPage
RecommendPanel
* hopefully not
No comments:
Post a Comment