EUnit a powerful unit testing framework for Erlang Richard Carlsson Mickaël Rémond.

39
EUnit a powerful unit testing framework for Erlang Richard Carlsson Mickaël Rémond

Transcript of EUnit a powerful unit testing framework for Erlang Richard Carlsson Mickaël Rémond.

Page 1: EUnit a powerful unit testing framework for Erlang Richard Carlsson Mickaël Rémond.

EUnit

a powerful unit testing framework for Erlang

Richard Carlsson

Mickaël Rémond

Page 2: EUnit a powerful unit testing framework for Erlang Richard Carlsson Mickaël Rémond.

What is unit testing?

● Testing “program units” in isolation Functions, modules, subsystems

● Testing specified behaviour (contracts) Input/output Stimulus/response Preconditions/postconditions, invariants

Page 3: EUnit a powerful unit testing framework for Erlang Richard Carlsson Mickaël Rémond.

What unit testing is not

● Unit testing does not cover: Performance testing Usability testing System testing Etc.

● Unit tests do not replace, but can be an important part of: Regression testing Integration testing

Page 4: EUnit a powerful unit testing framework for Erlang Richard Carlsson Mickaël Rémond.

Test-driven design

● Write unit tests (and run them, often) while developing the program, not afterwards

● Write tests for a feature before implementing that feature

● Move on to another feature when all tests pass● Good for focus and productivity:

Concentrate on solving the right problems Avoid overspecification and premature optimization

● Regression tests for free

Page 5: EUnit a powerful unit testing framework for Erlang Richard Carlsson Mickaël Rémond.

Unit testing frameworks

● Make it easy to: Write tests

● minimal typing overhead Run tests

● at the push of a button View test results

● efficient feedback

● Made popular by the JUnit framework for Java by Beck and Gamma.

Page 6: EUnit a powerful unit testing framework for Erlang Richard Carlsson Mickaël Rémond.

Functional unit testing

● The “xUnit family” of frameworks (JUnit, etc.) are mostly built on object-oriented principles Heavy use of inheritance:

● scaffolding (code for running the tests)● setup/teardown of test contexts (open/close files, etc.)

● We want to be able to handle tests as data Lambda expressions (funs) make natural test cases Represent test sets as collections (lists) of funs Deep lists make it easy to combine test sets

Page 7: EUnit a powerful unit testing framework for Erlang Richard Carlsson Mickaël Rémond.

Test case conventions

● A test case is represented as a fun that takes no arguments

● A test fails if evaluation throws an exception● Any return value is considered a success

Test = fun () -> ... end,try Test() of _ -> okcatch _:_ -> errorend

Page 8: EUnit a powerful unit testing framework for Erlang Richard Carlsson Mickaël Rémond.

Using EUnit

● Most basic usage:eunit:test(TestSet)

● Where TestSet can be: a fun (taking zero arguments) a module name various other things (as we shall see) a (deep) list of funs, module names, and

other things

Page 9: EUnit a powerful unit testing framework for Erlang Richard Carlsson Mickaël Rémond.

Modules as test sets

● Given a module name (any atom), EUnit will look for exported functions with special names:

..._test() % simple test

..._test_() % test generator● Simple test functions are treated just as funs:

they represent a single test case.● Test generators are called immediately, and

should return a test set (a fun, module name, list of tests, etc.)

Page 10: EUnit a powerful unit testing framework for Erlang Richard Carlsson Mickaël Rémond.

Using EUnit in your code

● The hard way: Add test functions (with conforming names)

● simple test functions (one test case per function)● generator functions, returning lists of funs

Export the functions Call eunit:test(Modulename)

● Using EUnit header files makes life easier Many useful macros Some additional magic

Page 11: EUnit a powerful unit testing framework for Erlang Richard Carlsson Mickaël Rémond.

EUnit utility macros

● -include_lib(“eunit/include/eunit.hrl”).

● Make the code more compact and readable● Generate more informative exceptions

?assert(1 + 1 == 2)

?assertMatch({foo, _}, make_foo(42))

?assertError(badarith, 1/0)

?assertThrow(ouch, throw(ouch))

Page 12: EUnit a powerful unit testing framework for Erlang Richard Carlsson Mickaël Rémond.

Fun-wrapper macros

● Macros whose names begin with “_” wrap their arguments in a fun-expression:

?_test(Expr) <=> fun()-> Expr end● Most test macros have _-forms:

?_test(?assert(BoolExpr)) <=> ?_assert(BoolExpr)

● Usage comparison:

simple_test()-> ?assert(BoolExpr).

fun_test_()-> ?_assert(BoolExpr).

Page 13: EUnit a powerful unit testing framework for Erlang Richard Carlsson Mickaël Rémond.

Developing a module with EUnit

-module(fib).-export([fib/1]).

fib(0) -> 1;fib(1) -> 1;fib(N) when N > 1 -> fib(N-1) * fib(N-2).

Page 14: EUnit a powerful unit testing framework for Erlang Richard Carlsson Mickaël Rémond.

Developing a module with EUnit

-module(fib).-export([fib/1]).-include_lib(“eunit/include/eunit.hrl”).

fib(0) -> 1;fib(1) -> 1;fib(N) when N > 1 -> fib(N-1) * fib(N-2).

Page 15: EUnit a powerful unit testing framework for Erlang Richard Carlsson Mickaël Rémond.

Test functions are auto-exported

-module(fib).-export([fib/1]).-include_lib(“eunit/include/eunit.hrl”).

fib(0) -> 1;fib(1) -> 1;fib(N) when N > 1 -> fib(N-1) * fib(N-2).

fib0_test() -> ?assert(fib(0) == 1).

Page 16: EUnit a powerful unit testing framework for Erlang Richard Carlsson Mickaël Rémond.

Test functions are auto-exported

-module(fib).-export([fib/1]).-include_lib(“eunit/include/eunit.hrl”).

fib(0) -> 1;fib(1) -> 1;fib(N) when N > 1 -> fib(N-1) * fib(N-2).

fib0_test() -> ?assert(fib(0) == 1).

fib1_test() -> ?assert(fib(1) == 1).

Page 17: EUnit a powerful unit testing framework for Erlang Richard Carlsson Mickaël Rémond.

Test functions are auto-exported

-module(fib).-export([fib/1]).-include_lib(“eunit/include/eunit.hrl”).

fib(0) -> 1;fib(1) -> 1;fib(N) when N > 1 -> fib(N-1) * fib(N-2).

fib0_test() -> ?assert(fib(0) == 1).

fib1_test() -> ?assert(fib(1) == 1).

fib2_test() -> ?assert(fib(2) == 2).

Page 18: EUnit a powerful unit testing framework for Erlang Richard Carlsson Mickaël Rémond.

Compact code with generators

-module(fib).-export([fib/1]).-include_lib(“eunit/include/eunit.hrl”).

fib(0) -> 1;fib(1) -> 1;fib(N) when N > 1 -> fib(N-1) * fib(N-2).

fib_test_() ->[?_assert(fib(0) == 1), ?_assert(fib(1) == 1), ?_assert(fib(2) == 2)].

Page 19: EUnit a powerful unit testing framework for Erlang Richard Carlsson Mickaël Rémond.

Compact code with generators

-module(fib).-export([fib/1]).-include_lib(“eunit/include/eunit.hrl”).

fib(0) -> 1;fib(1) -> 1;fib(N) when N > 1 -> fib(N-1) * fib(N-2).

fib_test_() ->[?_assert(fib(0) == 1), ?_assert(fib(1) == 1), ?_assert(fib(2) == 2), ?_assert(fib(3) == 3), ?_assert(fib(4) == 5), ?_assert(fib(5) == 8)].

Page 20: EUnit a powerful unit testing framework for Erlang Richard Carlsson Mickaël Rémond.

Also test the error cases

-module(fib).-export([fib/1]).-include_lib(“eunit/include/eunit.hrl”).

fib(0) -> 1;fib(1) -> 1;fib(N) when N > 1 -> fib(N-1) * fib(N-2).

fib_test_() ->[?_assert(fib(0) == 1), ?_assert(fib(1) == 1), ?_assert(fib(2) == 2), ?_assert(fib(3) == 3), ?_assert(fib(4) == 5), ?_assert(fib(5) == 8), ?_assertError(function_clause, fib(-1))].

Page 21: EUnit a powerful unit testing framework for Erlang Richard Carlsson Mickaël Rémond.

Compiling and running

1> c(fib).

Page 22: EUnit a powerful unit testing framework for Erlang Richard Carlsson Mickaël Rémond.

Compiling and running

1> c(fib).{ok,fib}2>

Page 23: EUnit a powerful unit testing framework for Erlang Richard Carlsson Mickaël Rémond.

Compiling and running

1> c(fib).{ok,fib}2> eunit:test(fib).

Page 24: EUnit a powerful unit testing framework for Erlang Richard Carlsson Mickaël Rémond.

Reading error reports

1> c(fib).{ok,fib}2> eunit:test(fib).fib:12:fib_test_...*failed*::{error,{assertion_failed, [{module, fib}, {line,12, {expression, “fib ( 2 ) == 2”}, {expected, true}, {value, false}]},...================================================= Failed: 4 Aborted: 0 Skipped: 0 Succeeded: 33>

Page 25: EUnit a powerful unit testing framework for Erlang Richard Carlsson Mickaël Rémond.

Fixing errors

-module(fib).-export([fib/1]).-include_lib(“eunit/include/eunit.hrl”).

fib(0) -> 1;fib(1) -> 1;fib(N) when N > 1 -> fib(N-1) * fib(N-2).

fib_test() ->[?_assert(fib(0) == 1), ?_assert(fib(1) == 1), ?_assert(fib(2) == 2), ?_assert(fib(3) == 3), ?_assert(fib(4) == 5), ?_assert(fib(5) == 8), ?_assertError(function_clause, fib(-1))].

Page 26: EUnit a powerful unit testing framework for Erlang Richard Carlsson Mickaël Rémond.

Fixing errors

-module(fib).-export([fib/1]).-include_lib(“eunit/include/eunit.hrl”).

fib(0) -> 1;fib(1) -> 1;fib(N) when N > 1 -> fib(N-1) + fib(N-2).

fib_test() ->[?_assert(fib(0) == 1), ?_assert(fib(1) == 1), ?_assert(fib(2) == 2), ?_assert(fib(3) == 3), ?_assert(fib(4) == 5), ?_assert(fib(5) == 8), ?_assertError(function_clause, fib(-1))].

Page 27: EUnit a powerful unit testing framework for Erlang Richard Carlsson Mickaël Rémond.

Using the automatic test() function

3> c(fib).{ok,fib}4> fib:test().

Page 28: EUnit a powerful unit testing framework for Erlang Richard Carlsson Mickaël Rémond.

Using the automatic test() function

3> c(fib).{ok,fib}4> fib:test(). All 7 tests successfulok5>

Page 29: EUnit a powerful unit testing framework for Erlang Richard Carlsson Mickaël Rémond.

Advanced test descriptors

● Labels{“Label Text”, Tests}

Page 30: EUnit a powerful unit testing framework for Erlang Richard Carlsson Mickaël Rémond.

Advanced test descriptors

● Labels{“Label Text”, Tests}

● Timeouts{timeout, Seconds, Tests}

Page 31: EUnit a powerful unit testing framework for Erlang Richard Carlsson Mickaël Rémond.

Advanced test descriptors

● Labels{“Label Text”, Tests}

● Timeouts{timeout, Seconds, Tests}

● Running tests in subprocesses{spawn, Tests}

Page 32: EUnit a powerful unit testing framework for Erlang Richard Carlsson Mickaël Rémond.

Advanced test descriptors

● Labels{“Label Text”, Tests}

● Timeouts{timeout, Seconds, Tests}

● Running tests in subprocesses{spawn, Tests}{spawn, Node, Tests}

Page 33: EUnit a powerful unit testing framework for Erlang Richard Carlsson Mickaël Rémond.

Advanced test descriptors

● Labels{“Label Text”, Tests}

● Timeouts{timeout, Seconds, Tests}

● Running tests in subprocesses{spawn, Tests}{spawn, Node, Tests}

● Specifying the execution order{inparallel, Tests}

Page 34: EUnit a powerful unit testing framework for Erlang Richard Carlsson Mickaël Rémond.

Advanced test descriptors

● Labels{“Label Text”, Tests}

● Timeouts{timeout, Seconds, Tests}

● Running tests in subprocesses{spawn, Tests}{spawn, Node, Tests}

● Specifying the execution order{inparallel, Tests}{inorder, Tests}

Page 35: EUnit a powerful unit testing framework for Erlang Richard Carlsson Mickaël Rémond.

More advanced test descriptors

● Running system commands{cmd, “Command String”}

Page 36: EUnit a powerful unit testing framework for Erlang Richard Carlsson Mickaël Rémond.

More advanced test descriptors

● Running system commands{cmd, “Command String”}

● Generators{generator, Fun}{generator, Module, Function}

Page 37: EUnit a powerful unit testing framework for Erlang Richard Carlsson Mickaël Rémond.

More advanced test descriptors

● Running system commands{cmd, “Command String”}

● Generators{generator, Fun}{generator, Module, Function}

● Test setup and cleanup{setup, SetupFun, % () -> R::any() CleanupFun, % (R::any()) -> any() (Instantiator % (R::any()) -> Tests | Tests)}

(setup runs the tests in a new process by default)

Page 38: EUnit a powerful unit testing framework for Erlang Richard Carlsson Mickaël Rémond.

And some even more advanced

● Repeated setup and cleanup{foreach, SetupFun, CleanupFun, [ Instantiator|Tests ]}

{foreachx, SetupFunX, CleanupFunX, [ {X::any(), InstantiatorX|Tests} ]

Page 39: EUnit a powerful unit testing framework for Erlang Richard Carlsson Mickaël Rémond.

End