05.11.07
Apache vs. Yaws
Ok. I know I already posted today, but this was just cool. A performance diagram comparing Apache to Yaws (An Erlang webserver). Apache dies at around 4,000 parallel sessions, while yaws still works at 80,000. Amazing.

Ok. I know I already posted today, but this was just cool. A performance diagram comparing Apache to Yaws (An Erlang webserver). Apache dies at around 4,000 parallel sessions, while yaws still works at 80,000. Amazing.
I recently was pointed to this article by pragdave about test-first development in Erlang with EUnit. I’m going to use his examples to show you how it would be done in ErUnit instead. First, some history…
The reason I wrote ErUnit instead of just using EUnit is for a couple of reasons
A huge benefit of going with EUnit is that it is MUCH more mature than ErUnit. I’m still tweaking it as need arises. One thing that I’m not sure if it really matters yet is all tests are run in their own thread, concurrently. I did that because it seemed super easy and fun to do, but I don’t know if you really get any benefit with that yet…
Well, on to creating a word wrap utility, test first, with ErUnit.
When creating a ErUnit test, you first name the file starting with “test_”. In our example below, when testing “text.erl”, we create the test file “test_text.erl”. ErUnit, with erunit_suite, has the convention of either:
This allows you to break up the tests from the code it’s testing pretty easily. Within the test class, you have to add the erunit_test behaviour. Behaviours in Erlang (well, technically the OTP library in Erlang) are used to define what callback functions are required. In this case, the erunit_test callback requires a function call “tests” with no arguments to be exported.
I personally also add a no argument “run” function that just delegates to erunit:run() with the tests passed in. I also like to import all of the erunit functions I’m going to be using (test/2, assertEquals/2, run/1) and the functions I’m going to be testing. This makes the code later on easier to read.
The file so far is:
-module(test_text). -behaviour(erunit_test). -export([tests/0, run/0]). -import(erunit, [test/2, assertEquals/2, run/1]). -import(text, [wrap/1]). run() -> run(tests()).
As mentioned earlier, you must declare a no argument function called “tests”. All this returns is an array of tests. So, for the first example we want to test that the “wrap” function works with no words.
tests() ->
[
test("Should wrap no words", fun() ->
assertEquals([""], wrap([]))
end)
].
When you use the erunit:test/2 function, you pass in the description of what you’re testing and a fun/0 of the actual test. assert(Description, true-false-test), assertEquals(First, Second), assertEquals(Description, First, Second), and fail(Description) are all currently implemented. Behind the scenes, the “test” function spawns a new thread that will run the fun/0 and send the answer back to the current test runner thread.
The code for the “text” module is the same as in pragdave’s example, so I won’t repeat it here. In fact, I’m just going to show you the entire test file:
-module(test_text).
-behaviour(erunit_test).
-export([tests/0, run/0]).
-import(erunit, [test/2, assertEquals/2, run/1]).
-import(text, [wrap/1]).
run() ->
run(tests()).
tests() ->
[
test("Should wrap no words", fun() ->
assertEquals([""], wrap([]))
end),
test("Should wrap one word", fun() ->
assertEquals(["cat"], wrap(["cat"]))
end),
test("Should wrap two words", fun() ->
assertEquals(["cat dog"], wrap(["cat", "dog"]))
end),
test("Should wrap when more than 10 chars long", fun() ->
Expected = ["cat dog", "elk"],
assertEquals(Expected, wrap(["cat", "dog", "elk"]))
end),
test("Should allow word larger than 10 on its own line", fun() ->
Expected = ["cat dog", "hummingbird", "ibix"],
Actual = wrap(["cat", "dog", "hummingbird", "ibix"]),
assertEquals(Expected, Actual)
end)
].
The output when running from erl is:
1> test_text:run(). ..... ok 2>
When a test fails with this extra test added:
test("This will fail", fun() ->
erunit:fail("Called erunit:fail/1")
end),
…you get…
1> test_text:run().
....F.
-"This will fail" failed:
Called erunit:fail/1
fail
2>
To show an error, this is the test:
test("This will have an error", fun() ->
erunit:assert("This method does not work with a integer", wrap(1))
end),
…which produces…
1> test_text:run().
....E.
-"This will have an error" had error:
error:function_clause
{erunit,'-test_process/3-fun-0-',3}
fail
2>
…Not the best error message, but I’m working on it.
So, I just wrote about my new favorite language Erlang. When learning a new language, it usually helps me to work on a simple problem I’ve solved before to understand how things work. So, I grabbed one of the coding exercises we use in recruiting at t-dub (no, I’m not going to post about them). Now, as every good xp-er, I started writing my test. Or, I should say, started looking for an xUnit framework for Erlang. Nada.
So, I wrote one myself. Test-first, I might add. (Trust me, it doesn’t get much more meta than that).
Download me. (or svn co http://codeby.bestfriendchris.com/svn/ErUnit/tags/erunit-0.1.0/)
To install, put it in the Erlang code path (on my OSX machine, it is /opt/local/lib/erlang/lib). Also, if you want to use the erlang script, add the bin folder to your PATH.
To write tests, you just need to name your test file test_whatever_you_want and add the following to the file:
-behaviour(erunit_test). -export([tests/0]).
I’d show you some more example test code, but my code highlighter doesn’t support Erlang, and I don’t really have the time to hack a new language file together for it right now. So, I’ll link directly to the test case I used in writing ErUnit: test_erunit.erl
To run tests from the command line, type:
erunit
# run tests in the “tests” folder
erunit tests
To run the test from erl, type:
erunit_suite:run()
% run tests in the “tests” folder
erunit_suite:run(”tests”)
A few more notes:
Comments/Suggestions/Complaints?
So, the other day I was shopping around for a new language. Specifically, I was looking for a functional language that wouldn’t cause my brain to bleed (and not Scheme. Love it dearly, but I needed something new). Enter Erlang. I found out about this little guy from a new beta book from the prag guys called Programming Erlang, Software for a Concurrent World (totally recommend it).
Bullets:
I’d recommend checking out the prag book linked above and see for your self. In a bit I’m going to post about ErUnit, the xUnit testing framework I wrote for Erlang. In the mean time, here’s my initial stab at a TextMate Erlang bundle.