My Erlang project managed by rebar, it divides to different module.
-pro
|-- rel
|-- src
|-- test
|--- module1_tests.erl
|--- module2_tests.erl
and for each module*_tests.erl, Use Eunit Fixtures to setup environment. For example,
module1_test_() ->
{setup,
fun setup/0,
fun clean_up/1,
fun (SetupData) ->
[
test_function(SetupData)
]
end}.
setup() ->
pro:start(),
ok.
clean_up(_) ->
pro:stop().
And Makefile is:
test:
ERL_AFLAGS="-config rel/pro/etc/app.config" $(REBAR) compile eunit skip_deps=true
Here I encounter a problem, since I have many test module in test/, each test module will start and stop application for whole executing flow. Sometimes start application will be failed, told not find app.config configure file, not sure why.
So I think is there a way to start application before all test module?
Sounds like you performing kind of testing that far away from unit testing idea. Maybe, in this case, you should use common test framework?
From Erlang docs -config flag you used
(ERL_AFLAGS="-config rel/pro/etc/app.config")
has to be
ERL_AFLAGS="-config app"
Look at Fixtures: http://erlang.org/doc/apps/eunit/chapter.html#Fixtures
A quick and dirty way could also be to add something like this as the first test case.
init_test() ->
{ok, Apps} = application:ensure_all_started(myapp),
Related
I have an Erlang application that is a REST service. I have a relx file and when I launch:
rebar3 release
I get an executable
./_build/default/rel/myapp/bin/myapp
When I launch that, the service starts and I can hit my service on localhost:80.
Now I'm trying to write a test suite to test some API calls. In my common test init_per_suite(Config) function, I want to launch my app, something like:
-module(apitest_SUITE).
-include_lib("common_test/include/ct.hrl").
-export([all/0]).
-export([test1/1, init_per_suite/1, end_per_suite/1]).
all() -> [test1].
init_per_suite(Config) ->
%LAUNCH MY APP HERE!!!
%LAUNCHING ../../_build/default/rel/myapp/bin/myapp SEEMS WRONG TO ME
[].
end_per_suite(Config) ->
%KILL MY APP HERE!!!
ok.
test1(Config) ->
httpc:get("localhost:80"). %e.g.
What is the proper way to launch my release from this common_test suite and do this testing?
BTW I am launching my tests using
rebar3 ct
OK, I figured out a way to do this (not sure if it is the best way):
init_per_suite(Config) ->
{ok, _} = application:ensure_all_started(myapp),
[].
%tests that hit localhost:port..
end_per_suite(Config) ->
ok = application:stop(myapp).
I had the same problem. You method can start the application but can't load the sys.config file. If the application depends on some configuration in this file, it will be started fail.
I can't figure out how to automatically start an "deps" applciation in Chicago Boss.
Use applcation:which_applications() I can see a bunch started like "lager, ranch, cowboy ..etc etc". How do I start another one I added?
If I go to console and run application:start(oauth2) and it's ok. I can start it.
But I tried to change src/xxx.app.src and add it to {applications,[]}, but no go. Isn't this the place to load/start any application that mine depends on?
But I can't find how boss loads and start all the other applications either.
By the way, this oauth2 appliation doesn't contain an application-behavior file (oauth2_app.erl) so I don't know if this is related. Since I can manually start it ok.
So, confused. Any help would be appreciated.
Plus: I remember that I did start application by adding the application:start(xxx) into the /priv/init/xxx_01_news.erl init function but that's very hackish I think.
You can use rebar.config to add your dependency applications then edit boss.config file this way:
First: Add the dependency applications name in applications.
Second: Add their specific configurations as follows.
boss.config file:
[
{boss, [
{path, "./deps/boss"},
{applications, [your_app_name, your_app_dep_1, your_app_dep_2]},
{assume_locale, "en"},
%% ... other configuration
%% APPLICATION CONFIGURATIONS
{your_app_name, []},
{your_app_dep_1, []},
{your_app_dep_2, []}
].
Edit: This way CB expects another boss application so looks for the router file and warns if cannot find it. However starting the dependencies inside its init function is not a dirty way and I suggest to consider it.
I have an Erlang application based on Cowboy and I would like to test it.
Previously I used wooga's library etest_http for this kind of tasks, but I would like to start using common tests since I noticed that this is the way used in cowboy. I have tried to setup a very basic test but I am not able to run it properly.
Can anybody provide me a sample for testing the basic example echo_get and tell me what is the correct way to run the test from the console using the Makefile contained in the example?
The example make used only for build echo_get app. So to test echo_get app you can add test suite and call make && rebar -v 1 skip_deps=true ct (rebar should be in PATH) from shell.
Also you need etest and etest_http in your Erlang PATH or add it with rebar.config in your application. You can use httpc or curl with os:cmd/1 instead ehttp_test :)
test/my_test_SUITE.erl (full example)
-module(my_test_SUITE).
-compile(export_all).
-include_lib("common_test/include/ct.hrl").
% etest macros
-include_lib ("etest/include/etest.hrl").
% etest_http macros
-include_lib ("etest_http/include/etest_http.hrl").
suite() ->
[{timetrap,{seconds,30}}].
init_per_suite(Config) ->
%% start your app or release here
%% for example start echo_get release
os:cmd("./_rel/bin/echo_get_example start"),
Config.
end_per_suite(_Config) ->
%% stop your app or release here
%% for example stop echo_get release
os:cmd("./_rel/bin/echo_get_example stop")
ok.
init_per_testcase(_TestCase, Config) ->
Config.
end_per_testcase(_TestCase, _Config) ->
ok.
all() ->
[my_test_case].
my_test_case(_Config) ->
Response = ?perform_get("http://localhost:8080/?echo=saymyname"),
?assert_status(200, Response),
?assert_body("saymyname", Response).
ok.
The following starts the hello_world application and its dependencies, but uses the most recently compiled versions rather than the one in ./_rel; that may or may not be what you want, but it does avoid the timer:sleep(1000)
-module(hello_world_SUITE).
-include_lib("common_test/include/ct.hrl").
-export([all/0, init_per_suite/1, end_per_suite/1]).
-export([http_get_hello_world/1]).
all() ->
[http_get_hello_world].
init_per_suite(Config) ->
{ok, App_Start_List} = start([hello_world]),
inets:start(),
[{app_start_list, App_Start_List}|Config].
end_per_suite(Config) ->
inets:stop(),
stop(?config(app_start_list, Config)),
Config.
http_get_hello_world(_Config) ->
{ok, {{_Version, 200, _ReasonPhrase}, _Headers, Body}} =
httpc:request(get, {"http://localhost:8080", []}, [], []),
Body = "Hello World!\n".
start(Apps) ->
{ok, do_start(_To_start = Apps, _Started = [])}.
do_start([], Started) ->
Started;
do_start([App|Apps], Started) ->
case application:start(App) of
ok ->
do_start(Apps, [App|Started]);
{error, {not_started, Dep}} ->
do_start([Dep|[App|Apps]], Started)
end.
stop(Apps) ->
_ = [ application:stop(App) || App <- Apps ],
ok.
Use https://github.com/extend/gun
to run the provided example (considering 'gun' in in 'user' folder):
ct_run -suite twitter_SUITE.erl -logdir ./results -pa /home/user/gun/deps/*/ebin /home/user/gun/ebin
I have some apps in my cluster, I need to start some of them sometimes on different hosts.
The story is that the Erlang cluster is already running, so even though I have my .app resource file per application stating which applications should be started before mine, this only works to create a startup script, not to start an app in a already running node.
At the moment I have a custom routine that uses application:get_key(Application,applications) to extract the dependencies and start them separately before starting the given application.
I was wondering if there isn't a better way of doing this.
Since Erlang R16B02, there is also application:ensure_all_started.
Frankly, the standard tools for doing this in Erlang are unnecessarily annoying right now. I tend to put the following boiler-plate in my application callback module:
-module(myapp_app).
-export([start/0]).
start() -> a_start(myapp, permanent).
a_start(App, Type) ->
start_ok(App, Type, application:start(App, Type)).
start_ok(_App, _Type, ok) -> ok;
start_ok(_App, _Type, {error, {already_started, _App}}) -> ok;
start_ok(App, Type, {error, {not_started, Dep}}) ->
ok = a_start(Dep, Type),
a_start(App, Type);
start_ok(App, _Type, {error, Reason}) ->
erlang:error({app_start_failed, App, Reason}).
You can then add -s myapp_app to your erlang command line and this will start the app and all its dependencies recursively. Why this function isn't in the application module I don't know :)
There is a working example of this custom erlang app startup code in my Erlang Factory 2012 SFBay example app.
When starting the app outside of the startup script you do need to start the dependencies first. You could build the smarts to do this into the app itself so that when the app starts it will start any required dependencies before it needs them.
One place I've seen this done is in Mochiweb apps. The default app templates include code for loading dependencies on startup:
-module(some_app).
-export([start/0, stop/0]).
ensure_started(App) ->
case application:start(App) of
ok ->
ok;
{error, {already_started, App}} ->
ok
end.
%% #spec start() -> ok
%% #doc Start the some_app server.
start() ->
some_app_deps:ensure(),
ensure_started(crypto),
application:start(some_app).
%% #spec stop() -> ok
%% #doc Stop the some_app server.
stop() ->
application:stop(some_app).
If you write your app under "OTP Design Principles", you will have to make yourappname.app file, which will contains `applications' section. This section defines which other applications you want to be started before yours. Here is stated:
applications
All applications which must be started before this
application is started. systools uses this list to generate correct
boot scripts. Defaults to [], but note that all applications have
dependencies to at least kernel and stdlib.
So if you use releases, this dependency resolution will be solved by systools.
In the recent Erlang R14, inets' file httpd.hrl has been moved from:
src/httpd.hrl
to:
src/http_server/httpd.hrl
The Erlang Web framework includes the file in several places using the following directive:
-include_lib("inets/src/httpd.hrl").
Since I'd love the Erlang Web to compile with both versions of Erlang (R13 and R14), what I'd need ideally is:
-ifdef(OLD_ERTS_VERSION).
-include_lib("inets/src/httpd.hrl").
-else.
-include_lib("inets/src/http_server/httpd.hrl").
-endif.
Even if it possible to retrieve the ERTS version via:
erlang:system_info(version).
That's indeed not possible at pre-processing time.
How to deal with these situations? Is the parse transform the only way to go? Are there better alternatives?
Not sure if you'll like this hackish trick, but you could use a parse transform.
Let's first define a basic parse transform module:
-module(erts_v).
-export([parse_transform/2]).
parse_transform(AST, _Opts) ->
io:format("~p~n", [AST]).
Compile it, then you can include both headers in the module you want this to work for. This should give the following:
-module(test).
-compile({parse_transform, erts_v}).
-include_lib("inets/src/httpd.hrl").
-include_lib("inets/src/http_server/httpd.hrl").
-export([fake_fun/1]).
fake_fun(A) -> A.
If you're on R14B and compile it, you should have the abstract format of the module looking like this:
[{attribute,1,file,{"./test.erl",1}},
{attribute,1,module,test},
{error,{3,epp,{include,lib,"inets/src/httpd.hrl"}}},
{attribute,1,file,
{"/usr/local/lib/erlang/lib/inets-5.5/src/http_server/httpd.hrl",1}},
{attribute,1,file,
{"/usr/local/lib/erlang/lib/kernel-2.14.1/include/file.hrl",1}},
{attribute,24,record,
{file_info,
[{record_field,25,{atom,25,size}},
{record_field,26,{atom,26,type}},
...
What this tells us is that we can use both headers, and the valid one will automatically be included while the other will error out. All we need to do is remove the {error,...} tuple and get a working compilation. To do this, fix the parse_transform module so it looks like this:
-module(erts_v).
-export([parse_transform/2]).
parse_transform(AST, _Opts) ->
walk_ast(AST).
walk_ast([{error,{_,epp,{include,lib,"inets/src/httpd.hrl"}}}|AST]) ->
AST;
walk_ast([{error,{_,epp,{include,lib,"inets/src/http_server/httpd.hrl"}}}|AST]) ->
AST;
walk_ast([H|T]) ->
[H|walk_ast(T)].
This will then remove the error include, only if it's on the precise module you wanted. Other messy includes should fail as usual.
I haven't tested this on all versions, so if the behaviour changed between them, this won't work. On the other hand, if it stayed the same, this parse_transform will be version independent, at the cost of needing to order the compiling order of your modules, which is simple enough with Emakefiles and rebar.
If you are using makefiles, you can do something like
ERTS_VER=$(shell erl +V 2>&1 | egrep -o '[0-9]+.[0-9]+.[0-9]+')
than match the string and define macro in erlc arguments or in Emakefile.
There is no other way, AFAIK.