zf

zenflows testing
git clone https://s.sonu.ch/~srfsh/zf.git
Log | Files | Refs | Submodules | README | LICENSE

case.ex (5756B)


      1 defmodule Credo.Test.Case do
      2   @moduledoc """
      3   Conveniences for testing Credo custom checks and plugins.
      4 
      5   This module can be used in your test cases, like this:
      6 
      7       use Credo.Test.Case
      8 
      9   Using this module will:
     10 
     11   * import all the functions from this module
     12   * make the test case `:async` by default (use `use Credo.Test.Case, async: false` to opt out)
     13 
     14   ## Testing custom checks
     15 
     16   Suppose we have a custom check in our project that checks whether or not
     17   the "FooBar rules" are applied (one of those *very* project-specific things).
     18 
     19       defmodule MyProject.MyCustomChecks.FooBar do
     20         use Credo.Check, category: :warning, base_priority: :high
     21 
     22         def run(%SourceFile{} = source_file, params) do
     23           # ... implement all the "FooBar rules" ...
     24         end
     25       end
     26 
     27   When we want to test this check, we can use `Credo.Test.Case` for convenience:
     28 
     29       defmodule MyProject.MyCustomChecks.FooBarTest do
     30         use Credo.Test.Case
     31 
     32         alias MyProject.MyCustomChecks.FooBar
     33 
     34         test "it should NOT report expected code" do
     35           \"\"\"
     36           defmodule CredoSampleModule do
     37             # ... some good Elixir code ...
     38           end
     39           \"\"\"
     40           |> to_source_file()
     41           |> run_check(FooBar)
     42           |> refute_issues()
     43         end
     44 
     45         test "it should report code that violates the FooBar rule" do
     46           \"\"\"
     47           defmodule CredoSampleModule do
     48             # ... some Elixir code that violates the FooBar rule ...
     49           end
     50           \"\"\"
     51           |> to_source_file()
     52           |> run_check(FooBar)
     53           |> assert_issues()
     54         end
     55       end
     56 
     57   This is as simple and mundane as it looks (which is a good thing):
     58   We have two tests: one for the good case, one for the bad case.
     59   In each, we create a source file representation from a heredoc, run our custom check and assert/refute the issues
     60   we expect.
     61 
     62   ## Asserting found issues
     63 
     64   Once we get to know domain a little better, we can add more tests, typically testing for other bad cases in which
     65   our check should produce issues.
     66 
     67   Note that there are two assertion functions for this: `assert_issue/2` and `assert_issues/2`, where the first one
     68   ensures that there is a single issue and the second asserts that there are at least two issues.
     69 
     70   Both functions take an optional `callback` as their second parameter, which is called with the `issue` or the
     71   list of `issues` found, which makes it convenient  to check for the issues properties ...
     72 
     73       \"\"\"
     74       # ... any Elixir code ...
     75       \"\"\"
     76       |> to_source_file()
     77       |> run_check(FooBar)
     78       |> assert_issue(fn issue -> assert issue.trigger == "foo" end)
     79 
     80   ... or properties of the list of issues:
     81 
     82       \"\"\"
     83       # ... any Elixir code ...
     84       \"\"\"
     85       |> to_source_file()
     86       |> run_check(FooBar)
     87       |> assert_issue(fn issues -> assert Enum.count(issues) == 3 end)
     88 
     89   ## Testing checks that analyse multiple source files
     90 
     91   For checks that analyse multiple source files, like Credo's consistency checks, we can use `to_source_files/1` to
     92   create
     93 
     94       [
     95         \"\"\"
     96         # source file 1
     97         \"\"\",
     98         \"\"\"
     99         # source file 2
    100         \"\"\"
    101       ]
    102       |> to_source_files()
    103       |> run_check(FooBar)
    104       |> refute_issues()
    105 
    106   If our check needs named source files, we can always use `to_source_file/2` to create individually named source
    107   files and combine them into a list:
    108 
    109       source_file1 =
    110         \"\"\"
    111         # source file 1
    112         \"\"\"
    113         |> to_source_file("foo.ex")
    114 
    115       source_file2 =
    116         \"\"\"
    117         # source file 2
    118         \"\"\"
    119         |> to_source_file("bar.ex")
    120 
    121       [source_file1, source_file2]
    122       |> run_check(FooBar)
    123       |> assert_issue(fn issue -> assert issue.filename == "foo.ex" end)
    124   """
    125   defmacro __using__(opts) do
    126     async = opts[:async] != false
    127 
    128     quote do
    129       use ExUnit.Case, async: unquote(async)
    130 
    131       import Credo.Test.Case
    132     end
    133   end
    134 
    135   alias Credo.Test.Assertions
    136   alias Credo.Test.CheckRunner
    137   alias Credo.Test.SourceFiles
    138 
    139   @doc """
    140   Refutes the presence of any issues.
    141   """
    142   def refute_issues(issues) do
    143     Assertions.refute_issues(issues)
    144   end
    145 
    146   @doc """
    147   Asserts the presence of a single issue.
    148   """
    149   def assert_issue(issues, callback \\ nil) do
    150     Assertions.assert_issue(issues, callback)
    151   end
    152 
    153   @doc """
    154   Asserts the presence of more than one issue.
    155   """
    156   def assert_issues(issues, callback \\ nil) do
    157     Assertions.assert_issues(issues, callback)
    158   end
    159 
    160   @doc false
    161   # TODO: remove this
    162   def assert_trigger(issue, trigger) do
    163     Assertions.assert_trigger(issue, trigger)
    164   end
    165 
    166   #
    167 
    168   @doc """
    169   Runs the given `check` on the given `source_file` using the given `params`.
    170 
    171       "x = 5"
    172       |> to_source_file()
    173       |> run_check(MyProject.MyCheck, foo_parameter: "bar")
    174   """
    175   def run_check(source_file, check, params \\ []) do
    176     CheckRunner.run_check(source_file, check, params)
    177   end
    178 
    179   #
    180 
    181   @doc """
    182   Converts the given `source` string to a `%SourceFile{}`.
    183 
    184       "x = 5"
    185       |> to_source_file()
    186   """
    187   def to_source_file(source) when is_binary(source) do
    188     SourceFiles.to_source_file(source)
    189   end
    190 
    191   @doc """
    192   Converts the given `source` string to a `%SourceFile{}` with the given `filename`.
    193 
    194       "x = 5"
    195       |> to_source_file("simple.ex")
    196   """
    197   def to_source_file(source, filename) when is_binary(source) and is_binary(filename) do
    198     SourceFiles.to_source_file(source, filename)
    199   end
    200 
    201   @doc """
    202   Converts the given `list` of source code strings to a list of `%SourceFile{}` structs.
    203 
    204       ["x = 5", "y = 6"]
    205       |> to_source_files()
    206   """
    207   def to_source_files(list) when is_list(list) do
    208     Enum.map(list, &to_source_file/1)
    209   end
    210 end