Skip to content
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

Improving Functional Test With pytest Fixtures #75

Merged
merged 23 commits into from
Aug 11, 2023

Conversation

axiomcura
Copy link
Member

@axiomcura axiomcura commented Aug 7, 2023

modified from EmbeddedArtistry

Description

Thank you for your contribution to pycytominer!
Please succinctly summarize your proposed change.
What motivated you to make this change?

Please also link to any relevant issues that your code is associated with.


This PR introduces using pytest fixtures

The main goal of this PR is to leverage fixtures to make functional test much easier. Below is a diagram on how fixtures and helper functions are implemented in a single test.

functional-testing-diagram drawio

Simple diagram illustrating how functional tests work: starting for the right side, (red) is the fixture used to generate temporary testing directories per test, (yellow) is the functional test with all the steps done, (green) is the a helper function known as prepare_data() that does all the heavy lifting to transporting datasets into the testing folder.

The diagram illustrates the implementation of CytoSnake's functional tests. Initially, a fixture (depicted in red) serves as input for the functional test (depicted in yellow). Within this process, pytest identifies that the fixture includes a path where the test should occur, hence generating a temporary directory. Additionally, the fixture's teardown function ensures that pytest removes the testing directory once the test finishes. Overall, leveraging these fixtures eliminates the need for developers to manually create and delete temporary directories for every test.

Additionally, a new function called prepare_dataset() has been added. This function primarily serves to facilitate the easy selection and transfer of data to a testing directory. It requires just one input: the desired dataset's name. The dataset names correspond to the directory names located within cytosnake/tests/functional/dataset.

Additional changes

New module

  • New module created test_utils for storing general helper functions, making testing development much easier.

Documentation changes

  • The new test_utils module has now been added into the RTD documentation website source code

Future PR

A future pull request will cover the rationale, motivations, and the design process behind the implementation of functional tests in CytoSnake. This PR will also provide a standardized guide detailing how to effectively document functional tests on the Read the Docs (RTD) website.


What is the nature of your change?functional

  • Bug fix (fixes an issue).
  • Enhancement (adds functionality).
  • Breaking change (fix or feature that would cause existing functionality to not work as expected).
  • This change requires a documentation update.

Checklist

Please ensure that all boxes are checked before indicating that a pull request is ready for review.

  • I have read the CONTRIBUTING.md guidelines.
  • My code follows the style guidelines of this project.
  • I have performed a self-review of my own code.
  • I have commented my code, particularly in hard-to-understand areas.
  • I have made corresponding changes to the documentation.
  • My changes generate no new warnings.
  • New and existing unit tests pass locally with my changes.
  • I have added tests that prove my fix is effective or that my feature works.
  • I have deleted all non-relevant text in this pull request template.

@axiomcura axiomcura changed the title Func tests Improving Functional Test With pytest Fixtures Aug 7, 2023
@axiomcura axiomcura marked this pull request as ready for review August 7, 2023 19:40
@axiomcura axiomcura added the enhancement New feature or request label Aug 7, 2023
Copy link
Member

@MikeLippincott MikeLippincott left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great PR. The documentation in the code looks amazing. The .cli.md files are above me so I can't provide much feedback... if you have time and could explain in person, that would be great! Thank you

cytosnake/tests/functional/test_cli.py Outdated Show resolved Hide resolved
cytosnake/tests/test_utils.py Outdated Show resolved Hide resolved
@axiomcura axiomcura requested a review from d33bs August 8, 2023 17:21
Copy link
Member

@d33bs d33bs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work! I left a few comments and suggestions throughout this review. Please don't hesitate to let me know if you have any questions or concerns.

I'm marking this as a "comment" review for now as I wasn't able to successfully run tests from the changes in this PR (running into a FileNotFoundError when running pytest for tests test_multiplates_with_multi_platemaps, test_one_plate_one_platemap, and test_multiplate_maps_no_barcode).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking about the future of CytoSnake testing, would it be useful to provide these datasets without the functional distinction in the directory path? For example, would these ever be used for unit or integration testing?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good point. I think that's a great idea! When I was developing this, I had no idea how the datasets should be implemented because the unit and workflow tests were not developed yet.

cytosnake/tests/__init__.py Outdated Show resolved Hide resolved
cytosnake/tests/functional/test_cli.py Outdated Show resolved Hide resolved
cytosnake/tests/functional/test_cli.py Outdated Show resolved Hide resolved
cytosnake/tests/functional/test_cli.py Outdated Show resolved Hide resolved
cytosnake/tests/functional/test_cli.py Outdated Show resolved Hide resolved
cytosnake/tests/functional/test_cli.py Outdated Show resolved Hide resolved
cytosnake/tests/functional/test_cli.py Outdated Show resolved Hide resolved
cytosnake/tests/functional/test_cli.py Outdated Show resolved Hide resolved
@axiomcura axiomcura requested a review from d33bs August 10, 2023 16:39
@axiomcura
Copy link
Member Author

axiomcura commented Aug 10, 2023

@d33bs Mind giving this a second round of review?

Here are some changes that I have done:

  • I have moved the testing folder outside the module and placed it into the project root folder
  • I removed the zero based data files and added NF1 dataset
  • dataset folder is outside the functional test.
  • test_utils module has been moved into the utils section in CytoSnake
  • added more assertions for my robust testing

Copy link
Member

@d33bs d33bs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work in addressing the comments and making changes! I've left a few more comments and generally felt things looked good. Please don't hesitate to let me know if you have any questions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider removing this file and making use of the pyproject.toml file + style for this configuration. Using the pyproject.toml file would help reduce file/code additions and also help with test invocation from the project root directory.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done: 7b1291f

Comment on lines 222 to 227
assert proc.returncode == 0
assert data_folder.exists()
assert cytosnake_file.exists()
assert barcodes_in_datafolder.exists()
assert metadata_in_datafolder.exists()
assert all([str(plate_data).endswith(".sqlite") for plate_data in all_plates])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seeing how this pattern repeats, is there an opportunity for a common utility function for checking these things to help reduce code duplication?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done! f2cc176

Comment on lines 168 to 172
assert proc.returncode == 0
assert data_folder.exists()
assert cytosnake_file.exists()
assert metadata_in_datafolder.exists()
assert all([str(plate_data).endswith(".sqlite") for plate_data in all_plates])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given the addition of non-zero data for testing are there other more specific data-orientated tests which could be added here or elsewhere to help with validation? This could come in, for example, the form of data shape, types, unique values, etc.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, no, those tests will be done within the either unit testing or workflow testing. The functional tests just focus on emulating user experience with the CLI. For example, setting up your directory to be a project directory. No files are being open and read into memory in this step.

tests/functional/test_cli.py Show resolved Hide resolved
"""

# transfer data to testing folder
test_utils.prepare_dataset(test_data_name="nf1-data", test_dir_path=testing_dir)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seeing how this pattern repeats, would this be a good opportunity for a fixture?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking about this earlier and I feel like the design of using fixture that require parameters are a bit daunting and may be not easy for new developers.

Below is an example if prepare_dataset() becomes a fixture:

@pytest.mark.parametrize("prepare_data", ["nf1"], indirect=True)
def text_example(testing_dir, prepare_dataset) -> None:
    # testing code below

However, if a redesign of the testing framework is conducted, then I will consider this approach.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did this and the related files come from other locations (and include efforts from other people)? If so, consider adding a reference of some kind to help document their contributions to this project (for example, through a CITATION.cff file using the references key).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added: 1c6286d

tests/functional/pytest.ini Outdated Show resolved Hide resolved
@axiomcura
Copy link
Member Author

@d33bs I have added comments and suggestions. I'll be merging until the end of the day if you do not have any more comments or suggestions.

Thank you!

@axiomcura axiomcura merged commit bdd2339 into WayScience:main Aug 11, 2023
@axiomcura axiomcura deleted the func-tests branch August 24, 2023 21:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants