Skip to content

Conversation

mikecdavis
Copy link
Contributor

Summary

  • Removes mockito from LogbackVerifier
  • Adds EventHandlerRule for validating dispatched events.
  • Removes dependency on mocked classes for the majority of tests.
  • Some stylized refactoring to condense statements.

The overuse of Mockito has made the tests fairly brittle and also required a large amount of setup for each specific test. Moving to a more black box approach and verify the side-effects of the scenarios makes for much cleaner and more concise tests.

This is also a prerequisite for a larger change to the event processing in which we'll be batching requests to the log endpoint. If this refactor was not done, then we'd have to change all of the mock setups related to dispatching at that time which would erode confidence that we're maintaining parity when introducing that change.

@coveralls
Copy link

Pull Request Test Coverage Report for Build 1081

  • 4 of 11 (36.36%) changed or added relevant lines in 2 files are covered.
  • 12 unchanged lines in 2 files lost coverage.
  • Overall coverage decreased (-0.5%) to 89.16%

Changes Missing Coverage Covered Lines Changed/Added Lines %
core-api/src/main/java/com/optimizely/ab/event/LogEvent.java 2 9 22.22%
Files with Coverage Reduction New Missed Lines %
core-api/src/main/java/com/optimizely/ab/notification/NotificationCenter.java 1 80.43%
core-api/src/main/java/com/optimizely/ab/Optimizely.java 11 93.17%
Totals Coverage Status
Change from base Build 1080: -0.5%
Covered Lines: 3290
Relevant Lines: 3690

💛 - Coveralls

@coveralls
Copy link

coveralls commented Jun 5, 2019

Pull Request Test Coverage Report for Build 1093

  • 10 of 11 (90.91%) changed or added relevant lines in 2 files are covered.
  • 8 unchanged lines in 3 files lost coverage.
  • Overall coverage increased (+0.1%) to 89.753%

Changes Missing Coverage Covered Lines Changed/Added Lines %
core-api/src/main/java/com/optimizely/ab/event/LogEvent.java 8 9 88.89%
Files with Coverage Reduction New Missed Lines %
core-api/src/main/java/com/optimizely/ab/notification/NotificationCenter.java 1 91.3%
core-api/src/main/java/com/optimizely/ab/config/PollingProjectConfigManager.java 2 84.0%
core-api/src/main/java/com/optimizely/ab/Optimizely.java 5 94.79%
Totals Coverage Status
Change from base Build 1092: 0.1%
Covered Lines: 3311
Relevant Lines: 3689

💛 - Coveralls

Copy link
Contributor

@aliabbasrizvi aliabbasrizvi left a comment

Choose a reason for hiding this comment

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

Looks good to me. Mostly nits here.

event.getKey(),
visitor.getVisitorId(),
visitor.getAttributes().stream()
.filter(attribute -> !attribute.getKey().startsWith("$"))
Copy link
Contributor

Choose a reason for hiding this comment

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

nit. Would recommend changing to startsWith("$opt_") or ProjectConfig. RESERVED_ATTRIBUTE_PREFIX

private String visitorId;
private Map<String, ?> attributes;

public CanonicalEvent(String experimentKey, String variationKey, String eventName, String visitorId, Map<String, ?> attributes) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't these be experimentId and variationId?

@Override
public String toString() {
return new StringJoiner(", ", CanonicalEvent.class.getSimpleName() + "[", "]")
.add("experimentKey='" + experimentKey + "'")
Copy link
Contributor

Choose a reason for hiding this comment

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

experimentId= and variationId=

assertNull(actualVariation);
}

/**
* Verify that {@link Optimizely#activate(String, String)} handles the case where an unknown experiment
* (i.e., not in the config) is passed through and a {@link RaiseExceptionErrorHandler} is provided.
* DUPE? I think this is testing RaiseExceptionErrorHandler.
Copy link
Contributor

Choose a reason for hiding this comment

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

That is correct. It is testing that exception handler works.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since that's the case I'm going to move this to a unit test.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

RaiseExceptionErrorHandlerTest unit test, which doesn't exist atm.

@@ -1077,59 +634,29 @@ public void activateWithTypedAttributes() throws Exception {
* In this case, the activate call should not remove the unknown attribute from the given map.
*/
@Test
@Ignore("Kind of a silly test since the event factory strips these unknownAttributes out before sending.")
Copy link
Contributor

Choose a reason for hiding this comment

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

Why are we skipping this at the moment?

Copy link
Contributor

Choose a reason for hiding this comment

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

Also, I think the intention of the test is that unknown attributes are stripped.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The test was originally asserting that the unknownattribute were getting passed to the EventFactory:

// setup the attribute map captor (so we can verify its content)
ArgumentCaptor<Map> attributeCaptor = ArgumentCaptor.forClass(Map.class);

// verify that the event builder was called with the expected attributes
verify(mockEventFactory).createImpressionEvent(eq(validProjectConfig), eq(activatedExperiment),
    eq(bucketedVariation), eq(testUserId), attributeCaptor.capture());

Map<String, String> actualValue = attributeCaptor.getValue();
assertThat(actualValue, hasKey("unknownAttribute"));

Now the test is going to assert that it's stripped from the payload, so the semantics have changed a bit.


Map<String, String> testUserAttributes = new HashMap<String, String>();
if (datafileVersion >= 4) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Alright, this if condition makes no sense.

Copy link
Contributor

Choose a reason for hiding this comment

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

These Harry Potter references 🤦‍♂

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah it might not be necessary at all. More of the copy-paste that needs to be purged.

}

/**
* Verify that {@link Optimizely#track(String, String, Map)}
* dispatches events even if the event links only to launched experiments
*/
@Test
// @Ignore("Is this a bug??")
Copy link
Contributor

Choose a reason for hiding this comment

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

Not a bug. Launched state works like experiment is running, but impression event is not dispatched during activate.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Need to remove that. I was investigating the test setup and got it working.

}

/**
* Verify that {@link Optimizely#track(String, String, Map)}
* dispatches log events when the tracked event links to both launched and running experiments.
*
* TODO Is this test needed? Feels unnecessary in a post EET world.
Copy link
Contributor

Choose a reason for hiding this comment

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

+1

assertEquals(testUserAttributes.get(entry.getKey()), entry.getValue());
}

assertEquals(event.getRequestMethod(), RequestMethod.GET);
Copy link
Contributor

Choose a reason for hiding this comment

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

Was this a bug?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The mocked logEventToDispatch was a GET, removing the mock and using the actual EventFactory meant this needed to change to POST.

@@ -1,6 +1,6 @@
/**
*
* Copyright 2016-2017, Optimizely and contributors
* Copyright 2016-2017, 2019 Optimizely and contributors
Copy link
Contributor

Choose a reason for hiding this comment

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

Updated even when no change?

@mikecdavis mikecdavis force-pushed the mikecdavis/OASIS-4843-refact-optimizely-test branch from 5169873 to 3472bfd Compare June 6, 2019 06:01
Copy link
Contributor

@aliabbasrizvi aliabbasrizvi left a comment

Choose a reason for hiding this comment

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

Looks good.

Copy link
Contributor

@thomaszurkan-optimizely thomaszurkan-optimizely left a comment

Choose a reason for hiding this comment

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

LGTM

@mikecdavis mikecdavis force-pushed the mikecdavis/OASIS-4843-refact-optimizely-test branch from 01e6ee5 to 0b2ada2 Compare June 12, 2019 16:07
@mikecdavis mikecdavis merged commit 81238f1 into master Jun 12, 2019
@mikecdavis mikecdavis deleted the mikecdavis/OASIS-4843-refact-optimizely-test branch June 12, 2019 16:28
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.

4 participants