-
Notifications
You must be signed in to change notification settings - Fork 50
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactoring test suite using StreamData #919
base: master
Are you sure you want to change the base?
Refactoring test suite using StreamData #919
Conversation
ca92522
to
d1f562d
Compare
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## master #919 +/- ##
==========================================
- Coverage 68.62% 65.48% -3.14%
==========================================
Files 228 146 -82
Lines 6667 4288 -2379
==========================================
- Hits 4575 2808 -1767
+ Misses 2092 1480 -612 ☔ View full report in Codecov by Sentry. |
a386639
to
ac30b5e
Compare
15808f7
to
f8bc9a0
Compare
4412eeb
to
ef42039
Compare
d4fff9e
to
3fa3a8b
Compare
3fa3a8b
to
7299862
Compare
3c7cdd6
to
a046b19
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, some files are missing the copyright header
apps/astarte_appengine_api/test/astarte_appengine_api/v2_device_test.exs
Outdated
Show resolved
Hide resolved
apps/astarte_appengine_api/test/astarte_appengine_api/v2_device_test.exs
Outdated
Show resolved
Hide resolved
apps/astarte_appengine_api/test/astarte_appengine_api/v2_group_test.exs
Outdated
Show resolved
Hide resolved
apps/astarte_appengine_api/test/support_v2/generators/datetime.ex
Outdated
Show resolved
Hide resolved
apps/astarte_appengine_api/test/support_v2/generators/device.ex
Outdated
Show resolved
Hide resolved
apps/astarte_appengine_api/test/support_v2/generators/mapping.ex
Outdated
Show resolved
Hide resolved
"automaton_accepting_states" => :erlang.term_to_binary(:automaton_accepting_states), | ||
"automaton_transitions" => :erlang.term_to_binary(:automaton_transitions), | ||
"aggregation" => AggregationType.to_int(interface.aggregation), | ||
# TODO Mapping to the other table |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Leftover?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
currently, select!(:interface)
doesn't join with mapping table. It's need to be done
6492bbd
to
1ad8284
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's being done here is very good. In my opinion, the overall structure of tests can be improved for enhancing isolation and clarity. My suggestion is:
- A setup_all
part in which a realm - whose name is randomly generated - is created, for each test file (i.e. each ExUnit.Case). This allows for non-interference of tests when running in parallel.
- A setup
part, for each test, in which tables relevant for the test are populated and truncated on exit. This allows for non-interference of tests running in the same case and using the database.
- Note: cleanup is necessary only when tests change database state. For example, a test “fail to create group with invalid name” does not need a cleanup, as no group will be added to the db; a “succeeds to create group with valid input” will need cleanup.
In general, by looking at a single test it should be possible to clearly identify what is being created, what operation is being performed and what the expected output would be.
defmodule Astarte.AppEngine.API.V2DeviceTest do | ||
use ExUnit.Case, async: true | ||
use ExUnitProperties | ||
use Astarte.Test.Cases.Device |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This use
hides the setup step(s) needed to populate the db, so it is unclear where e.g. devices selected at line 39 do come from. It should be possible to clearly identify what is being created, what operation is being performed and what the expected output would be by just looking at a single test.
I find that explicitly implementing setup_all
and setup
in the test case helps doing so.
The same goes for the other tests on groups and interfaces.
### | ||
### Delete | ||
@delete_interface """ | ||
DELETE FROM :keyspace.interfaces WHERE name = :name |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If tests from different cases run in different keyspaces and, inside a case, each test populates the tables it needs and truncates them on exit, you get database isolation for free, without the need to write more complex queries for deleting only some data.
A quick sketch of a possible way of handling it:
defmodule MyTest do
use ExUnit.Case
setup_all do
create_keyspace!("keyspace_#{System.unique_integer()}")
end
setup do
# get keyspace from context
insert!(keyspace, devices_fixture())
on_exit(fn -> run!("TRUNCATE TABLE devices") end)
end
test "my test 1" do
# use the `devices` table
end
end
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have already discussed this with @rbino. The point is to write as little code as possible in each test file. Cases are made to achieve this
def init(%{device_count: device_count, interfaces: interfaces}) do | ||
{:ok, devices: DeviceGenerator.device(interfaces: interfaces) |> Enum.take(device_count)} | ||
end | ||
|
||
def setup(%{cluster: cluster, keyspace: keyspace, devices: devices}) do | ||
on_exit(fn -> | ||
DatabaseHelper.delete!(:device, cluster, keyspace, devices) | ||
end) | ||
|
||
DatabaseHelper.insert!(:device, cluster, keyspace, devices) | ||
:ok | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These two can be moved in (or called from) a setup
block , instead of being in a different module
setup_all [ | ||
{DeviceSetup, :init}, | ||
{DeviceSetup, :setup} | ||
] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that setup_all
runs once before all tests in a case. In order to improve test isolation and clarity, it would be better to generate device data for each test (i.e. with setup
).
The same goes also for groups and interfaces
Changed - using exandra -- waiting for database project - implemented jwt + public/private key - wip group tests - squashed all failure - polishing - removed one_of_constant -> member_of - splitted tests - WIP device generator - completed device generator...but using opaque data (TODO change it) - basic datetime generator - WIP device generator - using Keyword.validate! - WIP insert into db, IPNET error - completed device WIP id doesn't match - completed base tests device - polishing - renamed realm -> keyspace - completed group generator + setup + case - wip group test (group_name "!" doesn't work) - added encoded_id field to device test - unlinked Conn <-> Database cases - fixed realm name - fixed valid group name test - added already exists - polished - removed common.ex Signed-off-by: Gabriele Ghio <[email protected]>
1ad8284
to
ef71037
Compare
TODO
refactoring test suite