Permalink
Browse files

add rspec like instance variables

closes #17.
  • Loading branch information...
1 parent e013a91 commit 38c2b53aaea83e183d55d88ba7497706d6342a0b @benmmurphy benmmurphy committed Dec 27, 2011
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`.
* `?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` 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([
spec/0
]).
+-import(espec_helper, [spec_set/2, spec_get/1]).
-define(assertEqual(Expected, Expr),
((fun (__Expected) ->
View
@@ -36,7 +36,8 @@ run_spec(Mod, Spec) ->
run_spec(Mod, Spec, ListenerState0, ListenerModule) ->
ExecutionTree = espec_ast:convert_to_execution_tree(Spec),
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).
pop_error_stack(Result0, FailTestDepth0) ->
@@ -52,35 +53,40 @@ push_error_stack(Result0, FailTestDepth0) ->
_ -> FailTestDepth0 + 1
end.
-run_execution_tree([], ListenerState0, _, _, _) ->
+run_execution_tree([], 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),
FailTestDepth = push_error_stack(Result, FailTestDepth0),
- run_execution_tree(ExecutionTree, ListenerState1, ListenerModule, Result, FailTestDepth);
-run_execution_tree([{end_group, _Line, GroupDescription} | ExecutionTree], ListenerState0, ListenerModule, Result0, FailTestDepth0) ->
+ run_execution_tree(ExecutionTree, ListenerState1, ListenerModule, Result, FailTestDepth,
+ [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),
{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),
- run_execution_tree(ExecutionTree, ListenerState1, ListenerModule, Result, FailTestDepth);
-run_execution_tree([{start_example, _Line, ExampleDescription} | ExecutionTree], ListenerState0, ListenerModule, Result, FailTestDepth0) ->
+ run_execution_tree(ExecutionTree, ListenerState1, ListenerModule, Result, FailTestDepth, ContextStack);
+run_execution_tree([{start_example, _Line, ExampleDescription} | ExecutionTree], ListenerState0, ListenerModule, Result, FailTestDepth0, ContextStack0) ->
+ NewContext = save_context(),
FailTestDepth = push_error_stack(Result, FailTestDepth0),
ListenerState1 = ListenerModule:start_example(ExampleDescription, ListenerState0),
- run_execution_tree(ExecutionTree, ListenerState1, ListenerModule, Result, FailTestDepth);
-run_execution_tree([{end_example, _Line, ExampleDescription} | ExecutionTree], ListenerState0, ListenerModule, Result0, FailTestDepth0) ->
+ run_execution_tree(ExecutionTree, ListenerState1, ListenerModule, Result, FailTestDepth, [NewContext | ContextStack0]);
+run_execution_tree([{end_example, _Line, ExampleDescription} | ExecutionTree], ListenerState0, ListenerModule, Result0, FailTestDepth0, [ContextHead|ContextRest]) ->
+ restore_context(ContextHead),
ListenerState1 = ListenerModule:end_example(ExampleDescription, Result0, ListenerState0),
{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),
- run_execution_tree(ExecutionTree, ListenerState0, ListenerModule, Result, FailTestDepth);
-run_execution_tree([{run, _} | ExecutionTree], ListenerState0, ListenerModule, Error, FailTestDepth) ->
+ run_execution_tree(ExecutionTree, ListenerState0, ListenerModule, Result, FailTestDepth, ContextStack);
+run_execution_tree([{run, _} | ExecutionTree], ListenerState0, ListenerModule, Error, FailTestDepth, ContextStack) ->
% 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) ->
try
@@ -124,3 +130,13 @@ filter_by_line(Line, EndLine, Body) ->
false ->
lists:map(fun({Part, _EndLine}) -> Part end, PartsWithEndLines)
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.