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

[Chore] Validate uniqueness of UI test keys #103

Merged
merged 5 commits into from
Jan 13, 2021

Conversation

CodeBaseCamp
Copy link
Contributor

Why
Executing the uiTest methods with duplicate keys causes previously created images to be overridden. This is usually undesirable and should be prevented programmatically.

Changes
A singleton UITestCaseKeyValidator instance for validating the uniqueness of UI test keys is introduced and used in the relevant methods.

Comment on lines 23 to 31
func validate(keys: Set<String>) {
guard self.keys.isDisjoint(with: keys) else {
fatalError("Key duplication: \(keys)")
}

keys.forEach {
self.keys.insert($0)
}
}
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 we add some locking?
Not sure if tests are executed in parallel right now, but it might be the case in the future

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good call, done.

/// the method fatally errs.
func validate(keys: Set<String>) {
guard self.keys.isDisjoint(with: keys) else {
fatalError("Key duplication: \(keys)")
Copy link
Contributor

Choose a reason for hiding this comment

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

Here we should print the intersection between self.keys and keys, not the whole keys

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

Copy link
Contributor

@FEC-bendingspoons FEC-bendingspoons left a comment

Choose a reason for hiding this comment

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

I left a couple of improvements, so that the usage of this is a bit more ergonomic. I have however some concerns:

  • are we sure that this is the best way to solve this problem? I feel that detecting these at runtime is too late (i.e. waiting for the uitest to finish before finding out). I feel that implementing a simple script that does a similar job in the linting phase is much more useful, as it wouldn't even let the project build
  • this is not thread safe. this is super bad, since test might (and will!) run in parallell
  • are your sure that a singleton is the best approach here? probably yes (since we are not the ones launching the tests) but it might be worth to investigate

static let singletonInstance = UITestCaseKeyValidator()

/// Set of keys with which the `validate` method of this instance has been invoked.
private var keys = Set<String>()
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
private var keys = Set<String>()
/// The keys are the uitest keys, the values are the test case that implements them
private var knownKeys: [String: String] = [:]

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.


/// Validates that the given `keys` are disjoint from any keys previously provided to this method. If a duplicate key is found,
/// the method fatally errs.
func validate(keys: Set<String>) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
func validate(keys: Set<String>) {
func validate(keys: Set<String>, testCaseName: String) {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

Comment on lines 24 to 30
guard self.keys.isDisjoint(with: keys) else {
fatalError("Key duplication: \(keys)")
}

keys.forEach {
self.keys.insert($0)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
guard self.keys.isDisjoint(with: keys) else {
fatalError("Key duplication: \(keys)")
}
keys.forEach {
self.keys.insert($0)
}
keys.forEach {
if let kownTestCaseName = self.knownKeys[$0] {
fatalError("Duplication detected. Key \($0) used both in \(testCaseName) and \(knownTestName)")
}
self.knownKeys[$0] = testCaseName
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

public extension ViewControllerTestCase where Self: XCTestCase {
func uiTest(testCases: [String: VC.V.VM], context: UITests.VCContext<VC>) {

UITestCaseKeyValidator.singletonInstance.validate(keys: Set(testCases.keys))
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
UITestCaseKeyValidator.singletonInstance.validate(keys: Set(testCases.keys))
UITestCaseKeyValidator.singletonInstance.validate(keys: Set(testCases.keys), testCaseName: "\(Self.self)")

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

public extension ViewTestCase where Self: XCTestCase {
func uiTest(testCases: [String: V.VM], context: UITests.Context<V>) {
UITestCaseKeyValidator.singletonInstance.validate(keys: Set(testCases.keys))
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
UITestCaseKeyValidator.singletonInstance.validate(keys: Set(testCases.keys))
UITestCaseKeyValidator.singletonInstance.validate(keys: Set(testCases.keys), testCaseName: "\(Self.self)")

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

@CodeBaseCamp
Copy link
Contributor Author

@FEC-bendingspoons Great comments, thanks!

are we sure that this is the best way to solve this problem? I feel that detecting these at runtime is too late (i.e. waiting for the uitest to finish before finding out). I feel that implementing a simple script that does a similar job in the linting phase is much more useful, as it wouldn't even let the project build

  • I initially thought about a script as well but we might miss cases (unless we make the script complicated) because the array of keys with which the uiTest methods are invoked may be defined before the actual invocation, so I think that the current solution is a good balance of the guarantee we want to achieve and the speed with which we find potential issues.

this is not thread safe. this is super bad, since test might (and will!) run in parallell

  • Totally agree, I fixed this.

are your sure that a singleton is the best approach here? probably yes (since we are not the ones launching the tests) but it might be worth to investigate

  • Using a singleton makes it easier to avoid mistakes arising from inserting keys to different instances, possibly missing a duplication. Do you have a better idea in mind?

@CodeBaseCamp
Copy link
Contributor Author

Also, @danyf90 thanks for pointing out the lacking thread-safety, I should have noticed this myself.

@LorDisturbia LorDisturbia self-assigned this Jan 13, 2021
@LorDisturbia LorDisturbia merged commit bcf9b3e into master Jan 13, 2021
@LorDisturbia LorDisturbia deleted the feature/ui-test-key-validation branch January 13, 2021 15:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants