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

DM-13567: Add tests to ap_pipe #40

Merged
merged 1 commit into from May 17, 2019
Merged

DM-13567: Add tests to ap_pipe #40

merged 1 commit into from May 17, 2019

Conversation

mrawls
Copy link
Collaborator

@mrawls mrawls commented May 3, 2019

Pair coded with @kfindeisen. The new tests require the ap_pipe_testdata package.

Copy link
Contributor

@parejkoj parejkoj left a comment

Choose a reason for hiding this comment

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

A variety of comments. I think the most important structural one is to see if you can refactor testReuseExistingOutput so that it doesn't have nested loops. Those can make debugging test failures a pain.

You need to add setupOptional(ap_pipe_testdata) to this package's ups table.

except pexExcept.NotFoundError:
raise unittest.SkipTest("ap_pipe_testdata not set up")

cls.config = cls._makeDefaultConfig()
Copy link
Contributor

Choose a reason for hiding this comment

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

I would have config be set in setUp, so that it is always reset for each test.


def makeMockDataRef(datasetType, level=None, dataId={}, **rest):
mockDataRef = Mock(dafPersist.ButlerDataRef)
mockDataRef.dataId = dict(dataId, **rest)
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm surprised that this worked! Guess I learned a new thing about dict initialization today.

with patch.object(task, "differencer") as mockDifferencer:
with patch.object(task, "diaSourceDpddifier") as mockDpddifier:
with patch.object(task, "associator") as mockAssociator:
with patch.object(task, "diaForcedSource") as mockForcedSource:
Copy link
Contributor

Choose a reason for hiding this comment

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

Ouch. You don't need to nest these, you can put them all in one context: with patch1() as mock1, patch2() as mock2, ...: doStuff().

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thank you, that works!

def testGenericRun(self):
# test the normal workflow of each ap_pipe step
task = ApPipeTask(self.butler, config=self.config)
inputRef = self.butler.dataRef("raw", **self.dataId)
Copy link
Contributor

Choose a reason for hiding this comment

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

I see this inputRef repeated below: can you put it into setUp?

forcedSource=mockForcedSource)

def testGenericRun(self):
# test the normal workflow of each ap_pipe step
Copy link
Contributor

Choose a reason for hiding this comment

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

Comment should be docstring?

for skippable in [
['ccdProcessor'],
['ccdProcessor', 'differencer'],
]:
Copy link
Contributor

Choose a reason for hiding this comment

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

Loops in tests make the tests difficult to untangle. You're only doing two things here, so I think it would be clearer if you made the internals of this into a separate method (e.g. checkSkippedTask) and then call it:

self.checkSkippedTask(['ccdProcessor'])
self.checkSkippedTask(['ccdProcessor', 'differencer'])

Similarly for the loop below, although I'm not sure how to unravel that one.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We fixed the first loop as suggested and added a message that identifies the subtask being tested for the second loop.

with self.mockPatchSubtasks(task) as subtasks:
# Self-subtraction ok because we're not actually doing any processing
task.runDataRef(inputRef, templateIds=[self.dataId])
subtasks.ccdProcessor.runDataRef.assert_called() # Should be twice
Copy link
Contributor

Choose a reason for hiding this comment

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

You can check assertEqual(runDataRef.call_count, 2) here.

task = ApPipeTask(self.butler, config=calexpConfig)
inputRef = self.butler.dataRef("raw", **self.dataId)
with self.mockPatchSubtasks(task) as subtasks:
# Self-subtraction ok because we're not actually doing any processing
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't understand what this comment is referring to?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Clarified. It was referring to the fact that the differencing template has the same dataId as the science template.


@contextlib.contextmanager
def mockPatchSubtasks(self, task):
"""Make mocks for all the ap_pipe subtasks.
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it's worth explaining in the docstring why this context manager is necessary: I was thinking that it wasn't because calls to mocks always return other mocks, so e.g. task.differencer.runDataRef is a mock. Except that task is not a mock, so you do need this. I thought you might be able to remove the context manager and put it into setup, patching ApPipeTask itself, but the subtasks don't exist until the Task is created, so you do need this.

So yes, some more explanation here would be good.

Parameters
----------
task : `lsst.ap.pipe.ApPipeTask`
the task whose subtasks will be mocked
Copy link
Contributor

Choose a reason for hiding this comment

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

Sentence.

@mrawls mrawls force-pushed the tickets/DM-13567 branch 3 times, most recently from 37ffb77 to 7253a4c Compare May 17, 2019 20:53
self.checkReuseExistingOutput(task, ['ccdProcessor', 'differencer'])

def checkReuseExistingOutput(self, task, skippable):
# Mock guarantees that all "has this been made" tests pass,
Copy link
Contributor

Choose a reason for hiding this comment

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

Docstring for this method?

@mrawls mrawls merged commit 13a2f15 into master May 17, 2019
@kfindeisen kfindeisen deleted the tickets/DM-13567 branch April 13, 2022 21:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants