Browsing the archives for the bdd tag.

Try, Try Again – What a Little Code Can Mean

work safe

I have started my Erlang based MUD a few times. The first time, I got bogged down in telnet details. The second time, I got lost building far too many proxy objects and indirections. Then I wasn’t even sure what I’d written was correct or that my changes wouldn’t break something.

I was having a hard time figuring out how to drive out the code. Where do I start?

This time around, I’ve gotten farther than I ever had before. I’m at a stage were I can move my character from one room to another. The perspective this time, I got a chance to drive the code out by running it via the REPL. I found a happy medium between top down and bottom up. This is similar to TDD use to test out an View-Model. My REPL based code had simple functions that mimicked the future texted based input. Seems like a duh now that I’ve said it. It really does feel more like I’m cutting with the grain.

But now, how do I make sure that I’ve got code that works constantly? Especially when I start dealing with more complex behaviors. I want to “record” my REPL sessions while focusing on building contexts since there are so many ways to build up a player and rooms. To do that, I’ve built a BDD micro framework based on E-Unit and a dash of rspec. I call it “test.hrl” and it is all of three macros!

-include_lib("eunit/include/eunit.hrl").
-define(It(Text,Func), {"It " ++ Text, Func}).
-define(It(Text,Setup,Cleanup,Func),
        {"It " ++ Text, setup,Setup,Cleanup,Func}).
-define(Describe(Text,Tests),{"Describe " ++ Text, Tests}).

It is based on the fact e-unit can used nested test descriptions to run tests. I just wrote some macros around it to make it fit my preconceptions of what a bdd framework should be like using terms I like.

When used, it looks like this

-module(player_tests).
-include("tests.hrl").

player_test_() ->[
  ?Describe("Bad Password",
    [?It("should return an error",fun setup/0,fun cleanup/1,
          ?_test(begin ?assertEqual(error, player:login("Tony", "BassPassword"))end))
    ]),
  ?Describe("Good Password",
    [?It("should have a player proxy",fun setup/0,fun cleanup/1,
                    ?_test(begin Me = player:login("Tony", "Hello"),
                                 ?assertEqual({ok,"You aint got jack!"},
                                 Me:inventory())
                           end))
     ]),
  ?Describe("Room Interaction",
    [?It("should describe the lobby",fun setup/0, fun cleanup/1,
         ?_test(begin Me = player:login("Tony", "Hello"),
                      ?assertEqual({ok, "It's a lobby"},
                                   Me:look())
                end)),
     ?It("should move to the kitchen", fun setup/0, fun cleanup/1,
         ?_test(begin Me = player:login("Tony", "Hello"),
                      Me:move("north"),
                      ?assertEqual({ok, "It's a kitchen"},
                                   Me:look()) end))])
].

setup() ->
        % I don't know why, but I need the print
        % to make the kitchen test pass
        io:format(""),
        stubs:fake_rooms().
cleanup(_Pid) ->
        stubs:stop_fake_rooms(),
        true.

Here’s my test runner

-module(test_runner).
-export([run/0]).
-include_lib("eunit/include/eunit.hrl").

run() ->
        eunit:test([player_tests],[verbose]).

The output looks like this


erl -noshell -pa ebin -s mnesia start -s test_runner run -s init stop
======================== EUnit ========================
module 'player_tests'
  Describe Bad Password
    It should return an error
      player_tests:7: player_test_...ok
      [done in 0.016 s]
    [done in 0.016 s]
  Describe Good Password
    It should have a player proxy
      player_tests:11: player_test_...ok
      [done in 0.015 s]
    [done in 0.015 s]
  Describe Room Interaction
    It should describe the lobby
      player_tests:18: player_test_...ok
      [done in 0.016 s]
    It should move to the kitchen
      player_tests:23: player_test_...ok
      [done in 0.016 s]
    [done in 0.032 s]
  [done in 0.063 s]
=======================================================
  All 4 tests passed.

This may not be perfect. I may not be the most ergonomic. I know it isn’t. But it’s close enough for me right now. I feel like I can describe what I want in a manner that fits me. Three macros and a slight change in how I view the world means I finally have this project moving forward well. It is truly amazing what just a little code can do.

No Comments