-
Notifications
You must be signed in to change notification settings - Fork 3
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
How-to create tests for module development purposes in plugin templates? #19
Comments
Good question. This is a first draft, and not finalized yet, so take the following with that in mind. Also, happy for any input on how to improve this, or other suggestions related to tests. Basically, we have 2 types of tests. "Normal" unit tests, which go under the 'tests' folder in a project folder, just create those as you would create normal pytest tests. Then there are sort of end-to-end module tests, which are supposed to make it easy to specify an operation and inputs, then test that the results are as expected. Technically, they are also run as pytest tests, and they are kicked of in the It works like this:
Those are yaml (or json) files with that look like: operation: logic.and
inputs:
a: true
b: false There are two mandatory keys:
At the moment, only scalar input types are supported, so in most cases you want to provide a pipeline that contains the operation you want to test, as well as other operations that create the inputs for that target operation from scalars. For example, this pipeline that imports csv files from a directory and converts it into a tables value: pipeline_name: import.tables.from.csv_files
doc: |
Create a tables value from a folder of csv files.
Each file will represent a table in the database.
steps:
- module_type: import.local.file_bundle
module_config:
include_file_types:
- ".csv"
step_id: import_csv_files
- module_type: create.tables.from.file_bundle
step_id: create_tables
input_links:
file_bundle: import_csv_files.file_bundle
input_aliases:
import_csv_files.path: path
output_aliases:
create_tables.tables: tables Could be saves under operation: "${this_dir}/../pipelines/tables_from_csv_files.yaml"
inputs:
path: "${this_dir}/../data/journals/" In this example I'm saving the job description as This is already a basic form of testing now, kiara will run all of the jobs in that folder when you push to Github via Github Actions, and if one of the jobs fail, the GH action wll complain. So stuff like invalid/outdated input fields or processing errors inside the module will be caught. The next step is to test the results against expected outputs. I will write about that later in another comment. |
2.) write expressions to test the results You can test your jobs manually with the commandline (or Python, but that's a bit more tedious in my opinion):
This will tell you whether your job will run successfully in principle, and mimic what will happen in the Github Action when pushing. In most cases we will also want to test that the result we are getting is actually correct, not just available. This is done by adding a folder that is named exactly like the job description file (without extension) to tables::properties::metadata.tables::tables::JournalEdges1902::rows: 321
tables::properties::metadata.tables::tables::JournalNodes1902::rows: 276 The test runner will test the result value against the expected value in this dictionary. The dictionary keys are assembled like:
You can check result properies, available keys, etc in the cli:
(this will only work from kiara>=0.5.6 onwards) This method of testing outputs does not support any non-scalar outputs, so in most cases testing properties is the only possible thing to do. If you have a scalar result, you can test against it using a dictionary key like: y::data: False ( For more in-depth tests, you can do those in Python directly. For that, instead (or in addition to) from kiara.models.values.value import Value
from kiara_plugin.tabular.models.tables import KiaraTables
def check_tables_result(tables: Value):
# we can check properties here like we did in the outputs.yaml file
# for that you need to look up the metadata Python classes, which is something that
# is not documented yet, not sure how to best do that
assert tables.get_property_data("metadata.tables").tables["JournalEdges1902"].rows == 321
# more interestingly, we can test the data itself
tables_data: KiaraTables = tables.data
assert "JournalEdges1902" in tables_data.table_names
assert "JournalNodes1902" in tables_data.table_names
edges_table = tables_data.get_table("JournalEdges1902")
assert "Source" in edges_table.column_names
assert "Target" in edges_table.column_names As I said, the testing (esp. the checking of results) is a work in progress, but it works reasonably well so far. I'd still like to get some input or more validation that the solution I have is reasonable before locking it in. |
One thing I forgot to mention, you can run the tests manually doing either:
or
|
Ah, and another thing that is relevant in the context of generating documentation. I tried to design the testing around example job descriptions, because I think we also should render the examples in the plugin documentation itself. In what form exactly (yaml files, python API examples, ...) is up for us to decide, but they contain all the information (esp. if we add well written 'doc' fields to them) to create a useful examples section for each plugin (or in other parts of the documentation). |
Some updates: to make writing those tests a bit more efficient, I've added support for a special 'init' example job description. This is a job description like described above, but it is run before any other job description invocation. This lets you prepare the kiara context in which the test runs with some example data (using the For reference, have a look at the kiara_plugin.tabular plugin, for example this test:
Any questions, just ask. |
Thanks a lot for the info, I am now trying to experiment, and as a first step, I am recapping via a to-do. If I understand right, the other insights in the current discussion item relate to the 2nd type of tests, which are the end-to-end module tests? (I will experiment on those first and it's possible that I will have additional questions once I am there) In the current question, I am trying to make sure that I understand the added value of creating unit tests (pytest / 1st type of tests you mentioned) in addition to the end-to-end module tests (2nd type of tests you mentioned). |
It's really not much different to when you write a normal Python application. You'd use pytests mainly to test utility functions, with different inputs & esp. edge cases. Since modules typically don't have a lot of code, you might not have any of those. End-to-end/integration tests are not that different really, and they are also run via pytest in our case, there is just this little framework around them that I described above to make it a bit easier. It's still important to test it with (meaningfully) different inputs & esp. edge cases. |
Concerning the support added for the special 'init' example job description, could you please specify from which version of Kiara? Thanks a lot |
Should be available with the latest, current version (0.5.9). |
In the case of the current workflow I am working on, data are onboarded from external sources: https://github.com/DHARPA-Project/kiara_plugin.topic_modelling/blob/develop/src/kiara_plugin/topic_modelling/modules/onboarding.py does that change something in the testing approach you described above? |
What was your previous testing approach? |
well, so far we hadn't a testing procedure in place - this is precisely the scope of this thread.. |
Ah well, then nothing changes I guess. You can still do your testing the same way it was possible before, but if you need pre-loaded data to test your module, this makes it easier because instead of your job refering to a pipeline, you can just use the input alias(es) of your 'init-ed' values in your job description, instead of writing a pipeline, refering to it in your job description, and use a local file path or url or whatever as job description input. For onboarding modules nothing would change, since those would not need such a pipeline and would have local paths/urls as job description inputs anyway. As I said, the tabular plugin can serve as an example of how such tests can be specified. |
My previous testing approach was to use and attach a Jupyter Notebook as I mentioned already in other threads, since Jupyter usage was, until now, my area of focus for Kiara usage as well as prototyping. This is a bit different now as these modules are now prepared to be used in a functional front-end app. From what I understand in general, for our users who are module creators, we should prompt them to do the tests as you described: 1) unit tests, if necessary, according to use cases, and 2) init job descriptions. As our users are not necessarily software engineers, we will need to document these tests in a user-friendly way, assuming no previous knowledge of testing processes. |
Yes, I agree. Not sure who's going to do that, but ideally someone who is on the same level as our supposed target audience here, so they are aware of what information needs to be provided. I guess we can't really document everything concerning testing, it's a non-trivial area of work, so for the more fundamental stuff we have to find good tutorials/docs on the internet and link to them. Anyone who's writing tests: make notes of the things that weren't clear to you or where you had difficulties when you wrote your first tests, so we can consider that for the content of this part of the documentation. |
Here's the result of my first experiment for this procedure (for an init job description test) I tried adding a test for one single module by creating an init.yaml file in the examples/jobs directory of the plugin I am working on.
for running the operation: this worked: I had the following error when trying while being in the jobs directory: "make: *** No rule to make target `test'. Stop." And from root directory of plugin, here's the error: Is there anything I should do before trying a |
Right, it seems you don't have pytest installed. You can do that is to pip install like:
In the project root. And yes, make commands always need to be run in the project root. |
oh, ok I assumed it was installed by default sorry,
(I was following this procedure for modules development https://dharpa.org/kiara.documentation/latest/extending_kiara/creating_modules/the_basics/#pre-loading-a-table-dataset ) |
Yes, correct. Those dependencies are not included in the default dependencies of a plugins dependencies, because if they were they would also be installed whenever an end-user installs it, which is something we don't want. I guess this is one of the things that applies to any Python project, not just kiara plugins. So maybe we can find some tutorial or similar we can link to. Or we write our own recommended 'create a dev environment' doc section if we decide to have one. |
well the tutorial I pointed to above is meant for users extending Kiara (so module developers) |
Right, yeah. |
Concerning:
The only valid names here are the output fields of the operation that the job uses (in the 'operation' field). You can get the available ones via
|
thanks! so just recapping, because when I tried as documented in the experiment feedback above, I also did the same for the inputs (operation_name__input_name) so I would like to modify the generic example for single operation testing below: correct version is:
Please correct if there's a mistake. If others try it, I think that it is worth noting that the input_name and output_name need to be exactly the same as |
Yes, correct. One thing to note is that the 'operation' can also be a path to a pipeline file, in which case kiara will run the pipeline. Pipelines are just 'special' operations, and they also have a set of input- and output-fields. Again, you can just use |
Yes, absolutely, and worth noting indeed. At the moment, this first experiment was specifically targeted for a single operation testing scenario, I will recap the same way at a later stage for the pipeline scenario. |
Is there a way to explore (from the CLI) data items created/saved via the |
maybe "table.augment_column" if I understand the concept right (I am borrowing your terminology with the "augment")? we would keep the initial column and augment the table with the new column, but maybe this is what the merge was? |
Ah, yes, sorry, you are right, we want to keep the old column, not replace it. I guess 'add_column' would be easiest to grasp for users, even though it's still tempting to think of the table as still 'being' the old table, just with a new column, instead of a newly created table that was created from the old table and an extra column. It's a fine distinction, but in the context of kiara I'd really like to understand users that they never ever modify data, only 'transform' into something new. But we have to balance that with usability, so I reckon 'add_column' would be good enough. How would the interface for that module look like? Just the original table, the new column, and an optional index where the column should be inserted, if that is not provided, it'll be attached to the end? And yes, that is possible with the |
Great, such a module would be really helpful indeed to help users assess the output in the context of the data. Indeed, the interface you described is precisely what seems to be needed. Optional index would be very useful for display purposes and, as you said, by default, appending the column at the end seems to make sense. Alright, thanks! I will proceed with the table.merge module then. And I will replace it later on. |
Ok, great. Implementing the module itself should be simple, so will have that ready sometime next week, hopefully along with proper releases of kiara itself and all the other plugins. |
On my side I have the |
Did you try the example code I linked in #18 (comment) ? This example merges a table with a (single) array (even though its a bit silly because it attaches the same array to the table it previously picked from it. In real life there would be some processing happening of course). Seems to work fine for me. |
Ah I see, I hadn't realized that such modules only appear via |
Update for job failuresTests can now also check for job failures. To do this, the job name (the name of the job description file) must contain the string 'fail'. If it does, kiara will expect an expection to be thrown, and fail the test if that doesn't happen. In addition, you can also check if the right exception is thrown (or the exception contains the right details), by adding one of the following files under
An example to match the complete string of the exception error message would be a file containing:
Or, if you want to check for the existence of one or multiple substrings, you can do like:
Alternatively, you can also test using python code (again, similar to check successful tests), using the argument name
additional job locationTests jobs can now, in addition to Checking test results stays the same, using a subfolder under This is done so we can have tests that check for exceptions (see above), without having them in the user-visible For examples how this is done in practice, check the |
Hi, I get the error message: But not sure where to add the comment in this scenario? |
Ah, good catch, it looks like the
But if the inputs are all there (as probably in your example), the
Anyway, long story short, you need the |
ok great, thank you! |
Hi, |
No best practice for that, you could just put them in a folder |
Alright great, thanks a lot! |
I created such a job (in a no test folder), like so:
when copying the path to this job and running it in the CLI, I am getting an error:
Have you had this kind of error in the past, do you have an idea why this may be? |
(I am getting the same error for another job that I created the same way) |
And those work if the job is in the 'examples/jobs' folder? |
I will try (this was after my last question), I am not saying that the location is the problem. I just don't know where the error could come from and where I should investigate. |
one of the modules that I want to test doesn't work anymore in the notebook, and I wanted to investigate (this is why I created the job) |
same error if I put it in former location. The command that I run to test the job is: |
Right, sorry, just wanted to make sure I understand the situation. Did you try with DEV=true ? |
a.k.a
That usually gives better error details etc. |
ok, I put it once after setting up the environment but I don't put it every time. trying that now. thanks |
I get indeed much more info, thanks, but still not sure where to investigate:
|
Is it a problem in my module? (most probably yes, but can I understand it via the error above?) |
Not sure, tbh. Looks like something wrong with your job file maybe? Maybe it's not valid yaml? Have you tried to run it directly, like:
|
This is probably not something for github issues. Slack instead? |
thanks, I just tried and got the same error |
Just to put this into writing, in case someone else has the same error: the issue was calling the |
Are there best practices to keep in mind or advice on tools to use to create tests while developing modules in plugin templates?
The text was updated successfully, but these errors were encountered: