Permalink
Browse files

add rspec like instance variables

closes #17.
  • Loading branch information...
benmmurphy committed Dec 27, 2011
1 parent e013a91 commit 38c2b53aaea83e183d55d88ba7497706d6342a0b
Showing with 88 additions and 16 deletions.
  1. +24 −0 README.md
  2. +1 −0 include/espec.hrl
  3. +32 −16 src/espec.erl
  4. +31 −0 src/espec_helper.erl
View
@@ -117,6 +117,30 @@ The supported assertions are as follows. These are compatible with those in EUni
* `?assertEqual(Expected, Expression)` - Ensure that `Expression` is equal to `Expected`. * `?assertEqual(Expected, Expression)` - Ensure that `Expression` is equal to `Expected`.
* `?assertMatch(Guard, Expression)` - Ensure that `Expression` matches `Guard`. * `?assertMatch(Guard, Expression)` - Ensure that `Expression` matches `Guard`.
## Configuration Variables
Espec supports configuration variables like rspec instance variables. These are meant to be set during the before filters
and accessed in the examples or in the after filters. The syntax is:
describe("instance variables", fun() ->
before_each(fun() ->
spec_set(var1, "variable 1")
end),
it("should do stuff", fun() ->
do_stuff(spec_get(var1))
end),
after_each(fun() ->
do_more_stuff(spec_get(var1))
end)
end).
If you modify an instance variable in an example the effects won't be seen in other examples. If you modify
an instance variable in a nested group the effects won't be seen in the outer scope. 'Before all' methods run before
all 'before each' methods so they can't see the effects of 'before each' methods. 'After all' methods will only be able
to see the effects of 'before all' methods and won't see effects from examples, 'before each' methods, or 'after each' methods.
## the `espec` command ## the `espec` command
The `espec` executable runs spec files and provides pretty output from the test results. You can pass it files or directories through which it will recursively look for spec files. The `espec` executable runs spec files and provides pretty output from the test results. You can pass it files or directories through which it will recursively look for spec files.
View
@@ -4,6 +4,7 @@
-export([ -export([
spec/0 spec/0
]). ]).
-import(espec_helper, [spec_set/2, spec_get/1]).
-define(assertEqual(Expected, Expr), -define(assertEqual(Expected, Expr),
((fun (__Expected) -> ((fun (__Expected) ->
View
@@ -36,7 +36,8 @@ run_spec(Mod, Spec) ->
run_spec(Mod, Spec, ListenerState0, ListenerModule) -> run_spec(Mod, Spec, ListenerState0, ListenerModule) ->
ExecutionTree = espec_ast:convert_to_execution_tree(Spec), ExecutionTree = espec_ast:convert_to_execution_tree(Spec),
ListenerState1 = ListenerModule:start_spec(Mod, ListenerState0), ListenerState1 = ListenerModule:start_spec(Mod, ListenerState0),
ListenerState2 = run_execution_tree(ExecutionTree, ListenerState1, ListenerModule, ok, 0), pristine_context(),
ListenerState2 = run_execution_tree(ExecutionTree, ListenerState1, ListenerModule, ok, 0, [[]]),
ListenerModule:end_spec(Mod, ListenerState2). ListenerModule:end_spec(Mod, ListenerState2).
pop_error_stack(Result0, FailTestDepth0) -> pop_error_stack(Result0, FailTestDepth0) ->
@@ -52,35 +53,40 @@ push_error_stack(Result0, FailTestDepth0) ->
_ -> FailTestDepth0 + 1 _ -> FailTestDepth0 + 1
end. end.
run_execution_tree([], ListenerState0, _, _, _) -> run_execution_tree([], ListenerState0, _, _, _, _) ->
ListenerState0; ListenerState0;
run_execution_tree([{start_group, _Line, GroupDescription} | ExecutionTree], ListenerState0, ListenerModule, Result, FailTestDepth0) -> run_execution_tree([{start_group, _Line, GroupDescription} | ExecutionTree], ListenerState0, ListenerModule, Result, FailTestDepth0, ContextStack0) ->
NewContext = save_context(),
ListenerState1 = ListenerModule:start_group(GroupDescription, ListenerState0), ListenerState1 = ListenerModule:start_group(GroupDescription, ListenerState0),
FailTestDepth = push_error_stack(Result, FailTestDepth0), FailTestDepth = push_error_stack(Result, FailTestDepth0),
run_execution_tree(ExecutionTree, ListenerState1, ListenerModule, Result, FailTestDepth); run_execution_tree(ExecutionTree, ListenerState1, ListenerModule, Result, FailTestDepth,
run_execution_tree([{end_group, _Line, GroupDescription} | ExecutionTree], ListenerState0, ListenerModule, Result0, FailTestDepth0) -> [NewContext | ContextStack0]);
run_execution_tree([{end_group, _Line, GroupDescription} | ExecutionTree], ListenerState0, ListenerModule, Result0, FailTestDepth0, [ContextHead|ContextTail]) ->
restore_context(ContextHead),
ListenerState1 = ListenerModule:end_group(GroupDescription, ListenerState0), ListenerState1 = ListenerModule:end_group(GroupDescription, ListenerState0),
{Result, FailTestDepth} = pop_error_stack(Result0, FailTestDepth0), {Result, FailTestDepth} = pop_error_stack(Result0, FailTestDepth0),
run_execution_tree(ExecutionTree, ListenerState1, ListenerModule, Result, FailTestDepth); run_execution_tree(ExecutionTree, ListenerState1, ListenerModule, Result, FailTestDepth, ContextTail);
run_execution_tree([{pending_example, _Line, ExampleDescription} | ExecutionTree], ListenerState0, ListenerModule, Result, FailTestDepth) -> run_execution_tree([{pending_example, _Line, ExampleDescription} | ExecutionTree], ListenerState0, ListenerModule, Result, FailTestDepth, ContextStack) ->
ListenerState1 = ListenerModule:pending_example(ExampleDescription, ListenerState0), ListenerState1 = ListenerModule:pending_example(ExampleDescription, ListenerState0),
run_execution_tree(ExecutionTree, ListenerState1, ListenerModule, Result, FailTestDepth); run_execution_tree(ExecutionTree, ListenerState1, ListenerModule, Result, FailTestDepth, ContextStack);
run_execution_tree([{start_example, _Line, ExampleDescription} | ExecutionTree], ListenerState0, ListenerModule, Result, FailTestDepth0) -> run_execution_tree([{start_example, _Line, ExampleDescription} | ExecutionTree], ListenerState0, ListenerModule, Result, FailTestDepth0, ContextStack0) ->
NewContext = save_context(),
FailTestDepth = push_error_stack(Result, FailTestDepth0), FailTestDepth = push_error_stack(Result, FailTestDepth0),
ListenerState1 = ListenerModule:start_example(ExampleDescription, ListenerState0), ListenerState1 = ListenerModule:start_example(ExampleDescription, ListenerState0),
run_execution_tree(ExecutionTree, ListenerState1, ListenerModule, Result, FailTestDepth); run_execution_tree(ExecutionTree, ListenerState1, ListenerModule, Result, FailTestDepth, [NewContext | ContextStack0]);
run_execution_tree([{end_example, _Line, ExampleDescription} | ExecutionTree], ListenerState0, ListenerModule, Result0, FailTestDepth0) -> run_execution_tree([{end_example, _Line, ExampleDescription} | ExecutionTree], ListenerState0, ListenerModule, Result0, FailTestDepth0, [ContextHead|ContextRest]) ->
restore_context(ContextHead),
ListenerState1 = ListenerModule:end_example(ExampleDescription, Result0, ListenerState0), ListenerState1 = ListenerModule:end_example(ExampleDescription, Result0, ListenerState0),
{Result, FailTestDepth} = pop_error_stack(Result0, FailTestDepth0), {Result, FailTestDepth} = pop_error_stack(Result0, FailTestDepth0),
run_execution_tree(ExecutionTree, ListenerState1, ListenerModule, Result, FailTestDepth); run_execution_tree(ExecutionTree, ListenerState1, ListenerModule, Result, FailTestDepth, ContextRest);
run_execution_tree([{run, Fun} | ExecutionTree], ListenerState0, ListenerModule, ok, 0 = FailTestDepth) -> run_execution_tree([{run, Fun} | ExecutionTree], ListenerState0, ListenerModule, ok, 0 = FailTestDepth, ContextStack) ->
Result = execute_test(Fun), Result = execute_test(Fun),
run_execution_tree(ExecutionTree, ListenerState0, ListenerModule, Result, FailTestDepth); run_execution_tree(ExecutionTree, ListenerState0, ListenerModule, Result, FailTestDepth, ContextStack);
run_execution_tree([{run, _} | ExecutionTree], ListenerState0, ListenerModule, Error, FailTestDepth) -> run_execution_tree([{run, _} | ExecutionTree], ListenerState0, ListenerModule, Error, FailTestDepth, ContextStack) ->
% Don't run a fun if there are existing errors % Don't run a fun if there are existing errors
run_execution_tree(ExecutionTree, ListenerState0, ListenerModule, Error, FailTestDepth). run_execution_tree(ExecutionTree, ListenerState0, ListenerModule, Error, FailTestDepth, ContextStack).
execute_test(Fun) -> execute_test(Fun) ->
try try
@@ -124,3 +130,13 @@ filter_by_line(Line, EndLine, Body) ->
false -> false ->
lists:map(fun({Part, _EndLine}) -> Part end, PartsWithEndLines) lists:map(fun({Part, _EndLine}) -> Part end, PartsWithEndLines)
end. end.
pristine_context() ->
put(espec_context, dict:new()).
save_context() ->
get(espec_context).
restore_context(Context) ->
put(espec_context, Context).
View
@@ -0,0 +1,31 @@
-module(espec_helper).
-export([spec_get/1, spec_set/2]).
spec_get(Atom) ->
case get(espec_context) of
undefined -> undefined;
ContextDict ->
get_value_or_undefined(Atom, ContextDict)
end.
get_value_or_undefined(Key, Dict) ->
case dict:is_key(Key, Dict) of
true ->
dict:fetch(Key, Dict);
_ ->
undefined
end.
spec_set(Atom, Value) ->
case get(espec_context) of
undefined ->
throw(espec_context_not_available);
ContextDict ->
% kind of space leaky if someone is repeatedly setting the same value
% but the alternative
put(espec_context, dict:store(Atom, Value, ContextDict)),
ok
end.

0 comments on commit 38c2b53

Please sign in to comment.