diff --git a/Main.hs b/Main.hs
--- a/Main.hs
+++ b/Main.hs
@@ -6,11 +6,14 @@
 import Control.Monad(replicateM)
 import System.Random
 import Control.Concurrent(threadDelay)
-import Data.List(intersperse)
+import Data.List(intercalate)
 import Data.Array
 
+-- Delay between iterations (μs)
 delay :: Int
 delay = 50000
+
+-- Probability of a cell being Alive on board generation
 density :: Float
 density = 0.37
 
@@ -21,7 +24,7 @@
   _ <- Curses.cursSet Curses.CursorInvisible
   Curses.timeout 0
   (rows, cols) <- Curses.scrSize
-  randomBoard (rows) (cols-1) >>= life window
+  randomBoard rows (cols-1) >>= life window
   Curses.resetParams
   Curses.endWin
 
@@ -40,7 +43,7 @@
   Curses.wRefresh window
 
 boardRep :: Board -> String
-boardRep board = concat . intersperse "\n" $ [rowStrings r | r <- [0..rs-1]]
+boardRep board = intercalate "\n" [rowStrings r | r <- [0..rs-1]]
   where
   rowStrings :: Int -> String
   rowStrings r = concat [show (grid ! r ! c) | c <- [0..cs-1]]
@@ -59,7 +62,7 @@
   randomRow = randomListOfCells >>= \cells -> return $ listArray (0, cs-1) cells
 
   randomListOfCells :: IO [State]
-  randomListOfCells = replicateM cs randomIO >>= return . map randomCell
+  randomListOfCells = fmap (map randomCell) (replicateM cs randomIO)
 
   randomCell :: Float -> State
   randomCell r
diff --git a/Simulation.hs b/Simulation.hs
--- a/Simulation.hs
+++ b/Simulation.hs
@@ -1,5 +1,7 @@
 module Simulation where
 
+-- Chose array for it's constant time random access
+-- (Haskell's List has linear time random access)
 import Data.Array
 
 data State = Alive | Dead deriving (Eq)
@@ -17,33 +19,40 @@
   show Alive = "■"
   show Dead = " "
 
-directions :: [Coord]
-directions = [Coord y x | y <- [-1..1], x <- [-1..1], not (x==0 && y==0)]
-
+-- Gets the coordinates of the cells that are alive around a
+-- coordinate
 neighbors :: Board -> Coord -> [Coord]
 neighbors board coord = filter (\c -> state board c == Alive) validCoords
   where
   validCoords = filter (validCoord board) coordsAround
   coordsAround = map (+ coord) directions
+  directions = [Coord y x | y <- [-1..1], x <- [-1..1], not (x==0 && y==0)]
 
+-- Determines whether a given coordinate is in bounds
 validCoord :: Board -> Coord -> Bool
 validCoord board (Coord y x) = xValid && yValid
   where 
   xValid = x >= 0 && x < bCols board
   yValid = y >= 0 && y < bRows board
 
+-- Get number of neighbors 
 numNeighbors :: Board -> Coord -> Int
 numNeighbors board coord = length $ neighbors board coord
 
+-- Gets the state of a coordinate on the board 
+-- (TODO: make this a total function)
 state :: Board -> Coord -> State
-state board (Coord y x) = (bGrid board) ! y ! x
+state board (Coord y x) = bGrid board ! y ! x
 
+-- Executes a single step for a coordinate on the board
 singleStep :: Board -> Coord -> State
 singleStep board coord = stepCell currentState bors
   where 
   currentState = state board coord
   bors = numNeighbors board coord
 
+-- Given the current state, and the # of alive neighbors,
+-- returns the new state of the cell
 stepCell :: State -> Int -> State
 stepCell Dead 3 = Alive
 stepCell Dead _ = Dead
@@ -52,6 +61,7 @@
   | n > 3 = Dead
   | otherwise = Alive
 
+-- Runs one iteration of game of life
 step :: Board -> Board
 step board = board {bGrid = newGrid}
   where
@@ -59,8 +69,8 @@
     newGrid = listArray (0, rs-1) $ map newRow [0..rs-1]
 
     newRow :: Int -> Row
-    newRow y = listArray (0,cs-1) $
-                  [singleStep board (Coord y x) | x <- [0..(cs)-1]]
+    newRow y = listArray (0,cs-1)
+                  [singleStep board (Coord y x) | x <- [0..cs-1]]
 
     cs = bCols board
     rs = bRows board 
diff --git a/game-of-life.cabal b/game-of-life.cabal
--- a/game-of-life.cabal
+++ b/game-of-life.cabal
@@ -2,7 +2,7 @@
 --  documentation, see http://haskell.org/cabal/users-guide/
 
 name:                game-of-life
-version:             0.1.0.4 
+version:             0.1.0.5 
 synopsis:            Conway's Game of Life
 -- description:         
 license:             MIT
@@ -20,7 +20,7 @@
   main-is:             Main.hs
   other-modules:       Simulation
   -- other-extensions:    
-  build-depends:       base >=4.7 && <4.8,
+  build-depends:       base >=4.7 && <5,
                        random,
                        hscurses,
                        array,
@@ -28,3 +28,20 @@
   -- hs-source-dirs:      
   default-language:    Haskell2010
   -- ghc-options:         -O2
+
+test-suite spec
+  type:
+      exitcode-stdio-1.0
+  ghc-options:
+      -Wall
+  hs-source-dirs:
+      test,
+      ./
+  main-is:
+      Spec.hs
+  default-language:    Haskell2010
+  other-modules:       Simulation
+  build-depends:
+      base >=4.7 && <5
+    , hspec   == 2.*
+    , array
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,94 @@
+module Main where
+
+import Test.Hspec
+import Simulation
+import Data.Array
+
+main :: IO ()
+main = hspec spec
+
+spec :: Spec
+spec = do
+  describe "stepCell" $ do
+    it "revives dead cells if num_neighbors is 3" $ do
+      (stepCell Dead 3) `shouldBe` Alive
+
+    it "does not revive dead cells ∀ num_neighbors in [0,3) ∪ (3,8]" $ do
+      mapM_ (\x -> (stepCell Dead x) `shouldBe` Dead) ([0..2] ++ [4..8])
+
+    it "kills live cells ∀ num_neighbors in [0,1] ∪ [4,8]" $ do
+      mapM_ (\x -> (stepCell Alive x) `shouldBe` Dead) ([0..1] ++ [4..8])
+
+    it "keeps cells alive ∀ num_neighbors in [2,3]" $ do
+      mapM_ (\x -> (stepCell Alive x) `shouldBe` Alive) [2,3]
+
+  describeStep
+
+describeStep :: Spec
+describeStep = do
+  describe "step" $ do
+    it "should handle a very simple board" $ do
+      let start = [ 
+                    [0, 0, 0],
+                    [0, 1, 0],
+                    [0, 0, 0] 
+                  ]
+      let expected_end = [
+                    [0, 0, 0],
+                    [0, 0, 0],
+                    [0, 0, 0] 
+                  ]
+      shouldBe True $ 
+        test_game start 1 expected_end
+                  
+    it "Should progress correctly" $ do
+      let zeroth_step = [
+                      [0, 1, 0],
+                      [1, 1, 1],
+                      [0, 1, 0] 
+                    ]
+      let first_step = [
+                     [1, 1, 1],
+                     [1, 0, 1],
+                     [1, 1, 1] 
+                   ]
+      let second_step = [
+                     [1, 0, 1],
+                     [0, 0, 0],
+                     [1, 0, 1] 
+                   ]
+
+      let board = toBoard zeroth_step
+      let two_steps = fmap toTestFormat $ (take 3 . iterate step) board
+      shouldBe two_steps [zeroth_step, first_step, second_step]
+
+
+test_game :: [[Int]] -> Int -> [[Int]] -> Bool
+test_game input n expected = toTestFormat (iterate step board !! n) == expected
+  where
+    board = toBoard input
+
+-- Everything below is glue to convert to and from the test
+-- format (which is easier to type and see visually), and
+-- the actual type of the board
+
+toTestFormat :: Board -> [[Int]]
+toTestFormat board = (fmap . fmap) stateToInt $ elems (fmap elems (bGrid board))
+
+toBoard :: [[Int]] -> Board
+toBoard input = Board {bGrid = grid, bRows = rows, bCols = cols}
+  where
+    grid = listArray (0, length input-1) $ map rowToArray input
+    rowToArray row = listArray (0, length row-1) $ map intToState row
+    rows = length input
+    -- This is a partial function, but it's only used for
+    -- tests and an empty array isn't valid input
+    cols = length (input !! 0)
+
+stateToInt :: State -> Int
+stateToInt Alive = 1
+stateToInt Dead  = 0
+
+intToState :: Int -> State
+intToState 1 = Alive
+intToState _ = Dead
