From cb4023d115c5cc5bb255d2f86618f68e4037b971 Mon Sep 17 00:00:00 2001 From: Anton Mishchuk Date: Mon, 4 Apr 2016 11:51:35 +0300 Subject: [PATCH] Check let in runtime #89 --- lib/espec.ex | 2 + lib/espec/example_helpers.ex | 3 +- lib/espec/let/checker.ex | 3 +- test/let/leaking_let_test.exs | 79 +++++++++++++++-------- test/let/leaking_subject_test.exs | 100 ++++++++++++++++++------------ 5 files changed, 117 insertions(+), 70 deletions(-) diff --git a/lib/espec.ex b/lib/espec.ex index 13efdaa5..db08cd13 100644 --- a/lib/espec.ex +++ b/lib/espec.ex @@ -18,6 +18,7 @@ defmodule ESpec do import ESpec.Context @context [%ESpec.Context{ description: inspect(__MODULE__), module: __MODULE__, line: __ENV__.line, opts: unquote(args) }] @defined_lets [] + import ESpec.ExampleHelpers import ESpec.DocTest, only: [doctest: 1, doctest: 2] @@ -42,6 +43,7 @@ defmodule ESpec do defmacro __before_compile__(_env) do quote do def examples, do: Enum.reverse(@examples) + def defined_lets, do: @defined_lets end end diff --git a/lib/espec/example_helpers.ex b/lib/espec/example_helpers.ex index 6d75e1a7..59251d32 100644 --- a/lib/espec/example_helpers.ex +++ b/lib/espec/example_helpers.ex @@ -21,9 +21,8 @@ defmodule ESpec.ExampleHelpers do opts: unquote(opts), file: __ENV__.file, line: __ENV__.line, context: context, shared: @shared} - ESpec.Let.Checker.check(@context, @defined_lets, unquote(escaped_block)) - def unquote(function)(var!(shared)) do + ESpec.Let.Checker.check(@context, __MODULE__, unquote(escaped_block)) var!(shared) unquote(block) end diff --git a/lib/espec/let/checker.ex b/lib/espec/let/checker.ex index 4d438e26..75ef23a1 100644 --- a/lib/espec/let/checker.ex +++ b/lib/espec/let/checker.ex @@ -1,5 +1,6 @@ defmodule ESpec.Let.Checker do - def check(context, defined_lets, escaped_block) do + def check(context, module, escaped_block) do + defined_lets = module.defined_lets context_lets = context_lets(context) diff = Enum.uniq(defined_lets) -- context_lets diff --git a/test/let/leaking_let_test.exs b/test/let/leaking_let_test.exs index 442f4c2e..d7e36c20 100644 --- a/test/let/leaking_let_test.exs +++ b/test/let/leaking_let_test.exs @@ -1,39 +1,64 @@ defmodule LeakingLetTest do use ExUnit.Case - @code1 (quote do - defmodule SomeSpec do - use ESpec - describe "first" do - let :a, do: 1 - it do: expect a |> to(eq 1) - end - describe "second" do - it do: expect a |> to(eq 1) - end + defmodule SomeSpec do + use ESpec + describe "first" do + let :a, do: 1 + it do: expect a |> to(eq 1) end - end) - - @code2 (quote do - defmodule SomeSpec do - use ESpec - describe "first" do - let :a, do: 1 - it do: expect a |> to(eq 1) - end + describe "second" do it do: expect a |> to(eq 1) end - end) + it do: expect a |> to(eq 1) + end - test "code1" do - assert_raise ESpec.LetError, "The let function `a/0` is not defined in the current scope!", fn -> - Code.compile_quoted(@code1) + defmodule SomeSpec2 do + use ESpec + describe "second" do + it do: a |> should(eq 1) + end + describe "first" do + let :a, do: 1 + it do: a |> should(eq 1) end end - test "code2" do - assert_raise ESpec.LetError, "The let function `a/0` is not defined in the current scope!", fn -> - Code.compile_quoted(@code2) - end + setup_all do + {:ok, + ex1: Enum.at(SomeSpec.examples, 0), + ex2: Enum.at(SomeSpec.examples, 1), + ex3: Enum.at(SomeSpec.examples, 2), + + ex4: Enum.at(SomeSpec2.examples, 0), + ex5: Enum.at(SomeSpec2.examples, 1), + } + end + + test "runs ex1 then ex2", context do + example = ESpec.ExampleRunner.run(context[:ex1]) + assert example.status == :success + + example = ESpec.ExampleRunner.run(context[:ex2]) + assert example.status == :failure + assert example.error.message =~ "The let function `a/0` is not defined in the current scope!" + end + + test "runs ex1 then ex3", context do + example = ESpec.ExampleRunner.run(context[:ex1]) + assert example.status == :success + + example = ESpec.ExampleRunner.run(context[:ex3]) + assert example.status == :failure + assert example.error.message =~ "The let function `a/0` is not defined in the current scope!" + end + + test "runs ex5 then ex4", context do + example = ESpec.ExampleRunner.run(context[:ex5]) + assert example.status == :success + + example = ESpec.ExampleRunner.run(context[:ex4]) + assert example.status == :failure + assert example.error.message =~ "The let function `a/0` is not defined in the current scope!" end end diff --git a/test/let/leaking_subject_test.exs b/test/let/leaking_subject_test.exs index bc61ccd4..0f0e1609 100644 --- a/test/let/leaking_subject_test.exs +++ b/test/let/leaking_subject_test.exs @@ -1,58 +1,78 @@ defmodule LeakingSubjectTest do use ExUnit.Case - @code1 (quote do - defmodule SomeSpec do - use ESpec - describe "first" do - subject do: [] - it "is empty by default", do: should be_empty - end + defmodule SomeSpec do + use ESpec - it do: should be_empty - end - end) + describe "first" do + subject do: [] - @code2 (quote do - defmodule SomeSpec do - use ESpec - describe "first" do - subject do: [] - it "is empty by default", do: should be_empty - end + it "is empty by default", do: should be_empty + end + describe "second" do + it do: should be_empty it do: is_expected |> to(be_empty) + it do: is_expected.to be_empty end - end) - - @code3 (quote do - defmodule SomeSpec do - use ESpec - describe "first" do - subject do: [] - it "is empty by default", do: should be_empty - end + end - it do: is_expected.to be_empty + defmodule SomeSpec2 do + use ESpec + describe "second" do + it do: should(eq 1) + end + describe "first" do + subject do: 1 + it do: should(eq 1) end - end) + end + setup_all do + {:ok, + ex1: Enum.at(SomeSpec.examples, 0), + ex2: Enum.at(SomeSpec.examples, 1), + ex3: Enum.at(SomeSpec.examples, 2), + ex4: Enum.at(SomeSpec.examples, 3), - test "code1" do - assert_raise ESpec.LetError, "The subject is not defined in the current scope!", fn -> - Code.compile_quoted(@code1) - end + ex5: Enum.at(SomeSpec2.examples, 0), + ex6: Enum.at(SomeSpec2.examples, 1), + } end - test "code2" do - assert_raise ESpec.LetError, "The subject is not defined in the current scope!", fn -> - Code.compile_quoted(@code2) - end + test "runs ex1 then ex2", context do + example = ESpec.ExampleRunner.run(context[:ex1]) + assert example.status == :success + + example = ESpec.ExampleRunner.run(context[:ex2]) + assert example.status == :failure + assert example.error.message =~ "The subject is not defined in the current scope!" end - test "code3" do - assert_raise ESpec.LetError, "The subject is not defined in the current scope!", fn -> - Code.compile_quoted(@code3) - end + test "runs ex1 then ex3", context do + example = ESpec.ExampleRunner.run(context[:ex1]) + assert example.status == :success + + example = ESpec.ExampleRunner.run(context[:ex3]) + assert example.status == :failure + assert example.error.message =~ "The subject is not defined in the current scope!" + end + + test "runs ex1 then ex4", context do + example = ESpec.ExampleRunner.run(context[:ex1]) + assert example.status == :success + + example = ESpec.ExampleRunner.run(context[:ex4]) + assert example.status == :failure + assert example.error.message =~ "The subject is not defined in the current scope!" + end + + test "runs ex6 then ex5", context do + example = ESpec.ExampleRunner.run(context[:ex6]) + assert example.status == :success + + example = ESpec.ExampleRunner.run(context[:ex5]) + assert example.status == :failure + assert example.error.message =~ "The subject is not defined in the current scope!" end end