Why does an ets table survive ct:init_per_testcase but not init_per_suite?

common-test, erlang, testing

Solution

As was already mentioned in the answer by Pascal and as discussed in the User Guide only `init_per_testcase` and `end_per_testcase` run in the same process as the testcase. Since ETS tables are bound to a owner process your only way to have a ETS table persist during a whole suite or group is to give it away or define a heir process.

You can easily spawn a process in your `init_per_suite` or `init_per_group` functions, set it as heir for the ETS table and pass its pid along in the config.

To clean up all you need is to kill this process in your `end_per_suite` or `end_per_group` functions.

-module(an_example_SUITE).
-include_lib("common_test/include/ct.hrl").

-compile(export_all).

all() -> [ets_tests].

ets_owner() ->
    receive
        stop -> exit(normal);
        Any -> ets_owner()
    end.

init_per_suite(Config) ->
    Pid = spawn(fun ets_owner/0),
    TabId = ets:new(conns, [set, protected, {heir, Pid, []}]),
    ets:insert(TabId, {foo, 2131}),
    [{table,TabId},{table_owner, Pid} | Config].

end_per_suite(Config) ->
    ?config(table_owner, Config) ! stop.

ets_tests(Config) ->
    TabId = ?config(table, Config),
    [{foo, 2131}] = ets:lookup(TabId, foo).

You also need to make sure you can still access your table from the testcase process, by making it either `protected`or `public`

Problem

I have a common test suite that attempts to create an ets table for use in all suites and all test cases. It looks like so: ``` -module(an_example_SUITE). -include_lib("common_test/include/ct.hrl"). -compile(export_all). all() -> [ets_tests]. init_per_suite(Config) -> TabId = ets:new(conns, [set]), ets:insert(TabId, {foo, 2131}), [{table,TabId} | Config]. end_per_suite(Config) -> ets:delete(?config(table, Config)). ets_tests(Config) -> TabId = ?config(table, Config), [{foo, 2131}] = ets:lookup(TabId, foo). ``` The `ets_tests` function failed with a badarg. Creating/destroying the ets table per testcase, which looks like so: ``` -module(an_example_SUITE). -include_lib("common_test/include/ct.hrl"). -compile(export_all). all() -> [ets_tests]. init_per_testcase(Config) -> TabId = ets:new(conns, [set]), ets:insert(TabId, {foo, 2131}), [{table,TabId} | Config]. end_per_testcase(Config) -> ets:delete(?config(table, Config)). ets_tests(Config) -> TabId = ?config(table, Config), [{foo, 2131}] = ets:lookup(TabId, foo). ``` Running this, I find that it functions beautifully. I'm confused by this behavior and unable to determine why this would happen, form the docs. Questions: - Why does this happen? - How can I have an ets table to share between a per suite and per testcase?

Original source