{"id":2128,"url":"https://github.com/metova/MetovaTestKit","last_synced_at":"2025-08-02T23:31:42.604Z","repository":{"id":54525209,"uuid":"58291382","full_name":"metova/MetovaTestKit","owner":"metova","description":"A collection of useful test helpers designed to ease the burden of writing tests for iOS applications.","archived":false,"fork":false,"pushed_at":"2021-02-12T21:23:38.000Z","size":851,"stargazers_count":23,"open_issues_count":6,"forks_count":2,"subscribers_count":16,"default_branch":"develop","last_synced_at":"2024-11-06T13:17:24.531Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/metova.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-05-08T01:26:13.000Z","updated_at":"2021-08-29T06:45:08.000Z","dependencies_parsed_at":"2022-08-13T18:40:26.417Z","dependency_job_id":null,"html_url":"https://github.com/metova/MetovaTestKit","commit_stats":null,"previous_names":[],"tags_count":18,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/metova%2FMetovaTestKit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/metova%2FMetovaTestKit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/metova%2FMetovaTestKit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/metova%2FMetovaTestKit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/metova","download_url":"https://codeload.github.com/metova/MetovaTestKit/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228503095,"owners_count":17930509,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-01-05T20:16:05.387Z","updated_at":"2024-12-06T17:30:36.219Z","avatar_url":"https://github.com/metova.png","language":"Swift","funding_links":[],"categories":["Testing"],"sub_categories":["Other Testing","Other free courses"],"readme":"[![MetovaTestKit](https://github.com/metova/MetovaTestKit/blob/master/Assets/MetovaTestKit.png?raw=true)](https://cocoapods.org/pods/MetovaTestKit)\n\n\u003cp align=\"center\"\u003e\n \u003ca href=\"https://travis-ci.org/metova/MetovaTestKit\" target=\"_blank\"\u003e\u003cimg src=\"https://travis-ci.org/metova/MetovaTestKit.svg?branch=master\" alt=\"Build Status\"\u003e\u003c/a\u003e \n \u003ca href=\"https://cocoapods.org/pods/MetovaTestKit\" target=\"_blank\"\u003e\u003cimg src=\"https://img.shields.io/cocoapods/v/MetovaTestKit.svg\" alt=\"CocoaPods Compatible\"/\u003e\u003c/a\u003e\n \u003ca href=\"http://metova.github.io/MetovaTestKit/\" target=\"_blank\"\u003e\u003cimg src=\"https://cdn.rawgit.com/metova/MetovaTestKit/master/docs/badge.svg\" alt=\"Documentation\"/\u003e\u003c/a\u003e\n \u003ca href=\"https://coveralls.io/github/metova/MetovaTestKit?branch=master\" target=\"_blank\"\u003e\u003cimg src=\"https://coveralls.io/repos/github/metova/MetovaTestKit/badge.svg?branch=master\u0026dummy=no_cache_please_1\" alt=\"Coverage Status\"/\u003e\u003c/a\u003e\n \u003ca href=\"http://cocoadocs.org/docsets/MetovaTestKit\" target=\"_blank\"\u003e\u003cimg src=\"https://img.shields.io/cocoapods/p/MetovaTestKit.svg?style=flat\" alt=\"Platform\"/\u003e\u003c/a\u003e\n \u003ca href=\"http://twitter.com/metova\" target=\"_blank\"\u003e\u003cimg src=\"https://img.shields.io/badge/twitter-@Metova-3CAC84.svg\" alt=\"Twitter\"/\u003e\u003c/a\u003e\n \u003cbr/\u003e\n\u003c/p\u003e\n \nMetovaTestKit is a collection of useful test helpers designed to ease the burden of writing tests for iOS applications.\n\n- [Requirements](#requirements)\n- [Installation](#installation)\n- [Usage](#usage)\n    - [MTKTestable Protocol](#mtktestable)\n    - [Testing UIKit Components](#testing-uikit-components)\n        - [UIAlertController](#uialertcontroller)\n        - [UIBarButtonItem](#uibarbuttonitem)\n        - [UIControl](#uicontrol)\n        - [UICollectionViewCell](#uicollectionviewcell)\n        - [UITableViewCell](#uitableviewcell)\n        - [UISegmentedControl](#uisegmentedcontrol)\n    - [Testing Dates](#testing-dates)\n    - [Testing Auto Layout Constraints](#testing-auto-layout-constraints)\n    - [Testing Exceptions](#testing-exceptions)\n    - [Asynchronous Testing](#asynchronous-testing)\n- [Documentation](#documentation)\n- [Credits](#credits)\n- [License](#license)\n\n-----\n\n# Requirements\n\n- Swift 4.0\n- iOS 9+\n\n-----\n\n# Installation\n\nMetovaTestKit is available through [CocoaPods](http://cocoapods.org).\n\nMetovaTestKit is intended to be used with unit testing targets. To install it, add MetovaTestKit your project's Podfile:\n\n```ruby\ntarget 'YourApp' do\n  # Your app's pods:\n  pod 'DataManager'\n  pod 'ThunderCats'\n  pod 'MetovaBase'\n\n  target 'YourAppTests' do\n    inherit! :search_paths\n    pod 'MetovaTestKit'\n  end\nend\n```\n\nAnd run `pod install`\n\nIf you would like to test a beta version of MetovaTestKit, you can install the latest from develop:\n\n```ruby\npod 'MetovaTestKit', :git =\u003e 'https://github.com/metova/MetovaTestKit.git', :branch =\u003e 'develop'\n```\n\n-----\n\n# Usage\n\n## MTKTestable\n\nMetovaTestKit defines the `MTKTestable` protocol. Correct implementation of this protocol allows for functional unit testing. It abstracts away the set up and tear down code into extensions of the types you want to test, and allows for functional unit tests.\n\n```swift\nfunc testOutlets() {\n    HomeViewControllerClass.test { testVC in\n        XCTAssertNotNil(testVC.userameTextField)\n        XCTAssertNotNil(testVC.passwordTextField)\n        XCTAssertNotNil(testVC.loginButton)\n    }\n}\n```\n\nThe `test` function rethrows any errors thrown inside the `testBlock`, allowing you to leveraging `throw`ing test cases to more conveniently denote failures.\n\n```swift\nfunc testLogin() throws {\n    try HomeViewControllerClass.test { testVC in\n        try testVC.login(username: \"jimmythecorgi\", password: \"woofwoof123\")\n    }\n}\n```\n\n## Testing UIKit Components\n\n\n### UIAlertController\n\nVerify that a view controller presented an alert having a particular style, title, message, and actions.\n\n```swift\nMTKAssertAlertIsPresented(\n    by: testVC,\n    style: .alert,\n    title: \"Warning\",\n    message: \"Are you sure you want to delete this user?\",\n    actions: [\n        ExpectedAlertAction(title: \"Delete\", style: .destructive),\n        ExpectedAlertAction(title: \"Cancel\", style: .cancel)\n    ]\n)\n```\n\n### UIBarButtonItem\n\nVerify that a bar button item has the expected target/action pair and that the target actually responds to the selector that will be sent to it. \n\n```swift\nMTKAssertBarButtonItem(testVC.editBarButtonItem, sends: #selector(MyViewController.didTapEditButton(_:)), to: testVC) \n```\n\n### UIControl\n\nWith a single assertion, you can verify that your control actions are hooked up and that your target actually responds to the selector that will be sent to it. \n\n```swift\nMTKAssertControl(testVC.loginButton, sends: #selector(LoginViewController.didTapLoginButton(_:)), to: testVC, for: .touchUpInside, \"The login button should be hooked up to the login action.\") \n```\n\n\n### UICollectionViewCell\n\nAssert that a collection view returns a cell of a specific type for a given index path.  Pass a block of code to perform additional tests on the cell, if it exists.\n\n```swift\nMTKTestCell(in: collectionView, at: indexPath, as: MyCollectionViewCell.self) { testCell in \n    XCTAssertEqual(testCell.label.text, \"Hello World!\")\n}\n```\n\nSee [the tests](./MetovaTestKitTests/CellTestingTests/MTKCollectionViewTestCellTests.swift#L34) for examples of test failures this method can generate.\n\n### UITableViewCell\n\nAssert that a table view returns a cell of a specific type for a given index path.  Pass a block of code to perform additional tests on the cell, if it exists.\n\n```swift\nMTKTestCell(in: tableView, at: indexPath, as: MyTableViewCell.self) { testCell in\n    XCTAssertEqual(testCell.label.text, \"Hello World!\")\n}\n```\n\nSee [the tests](./MetovaTestKitTests/CellTestingTests/MTKTableViewTestCellTests.swift#L34) for examples of test failures this method can generate.\n\n### UISegmentedControl\n \nVerify that a `UISegmentedControl` has the segment titles you are expecting. \n \n```swift\nMTKAssertSegmentedControl(segmentedControl, hasSegmentTitles: [\"Followers\", \"Following\"])\n```\n \n## Testing Dates\n \nYou can use MetovaTestKit to assert two dates are equal when only considering specified components.\n\n```swift\nMTKAssertEqualDates(date1, date2, comparing: .year, .month, .day)\n```\n\nThis assertion accepts components as a variadic argument or as a `Set\u003cCalendar.Component\u003e`.\n\n```swift\nlet components: Set\u003cCalendar.Component\u003e = [.year, .month, .day, .hour, .minute, .second]\nMTKAssertEqualDates(date1, date2, comparing: components)\nMTKAssertEqualDates(date3, date4, comparing: components)\nMTKAssertEqualDates(date5, date6, comparing: components)\n```\n \n## Testing Auto Layout Constraints\n\nYou can use MetovaTestKit to assert that you do not have broken Auto Layout constraints.\n\n```swift\nMTKAssertNoBrokenConstraints {\n    // code that does anything that impacts the user interface\n    // including simply loading a view for the first time\n}\n```\n\nThis assertion will fail for any broken constraints and report the number of constraints that broke during the test. You can also pass a custom message.\n\n```swift\nMTKAssertNoBrokenConstraints(message: \"Constraints were broken.\") {\n    // code to test\n}\n```\n\nThis test also returns a value with a count of the number of constraints broken.\n\n```swift\nlet brokenConstraintCount = MTKAssertNoBrokenConstraints {\n    // code to test\n}\n```\n\n## Testing Exceptions\n\nYou can use MetovaTestKit to assert that code that should not throw exceptions doesn't. Without MetovaTestKit, this would result in the entire test suite crashing. With MetovaTestKit, this is just a failed test, and you still get to run the rest of the test suite.\n\n```swift\nMTKAssertNoException {\n    // code that should not throw exceptions\n    // results in passing test if no exceptions are thrown\n    // results in failing test if exceptions are thrown\n}\n```\n\nYou can also pass a message to print on failure.\n\n```swift\nMTKAssertNoException(message: \"Exception was thrown.\") {\n    // code that should not throw exceptions\n    // results in passing test if no exceptions are thrown\n    // results in failing test if exceptions are thrown\n}\n```\n\nYou can also test code to verify that exceptions are thrown, and can do this without crashing your test suite. If you do not care about the specific exception but only want to verify that the code block throws an exception, you can use `MTKAssertException`:\n\n```swift\nMTKAssertException {\n    // code that should throw exceptions\n    // results in passing test if an exception is thrown\n    // results in a failing test if this closure returns without throwing\n}\n```\n\nLike `MTKAssertNoException`, this function also accepts a message:\n\n```swift\nMTKAssertException(message: \"No exception was thrown.\") {\n    // code that should throw exceptions\n    // results in passing test if an exception is thrown\n    // results in a failing test if this closure returns without throwing\n}\n```\n\nThese methods do return the thrown exception in case you need more information about it.\n\n```swift\nguard let exception = MTKAssertException(testBlock: throwingBlock) else {\n    XCTFail(\"Block failed to throw an exception\")\n    return\n}\n\n// More assertion about the given exception that was returned\n```\n\n```swift\nif let exception = MTKAssertNoException(testBlock: blockThatShouldntThrow) {\n    XCTFail(\"Block should not have thrown but instead threw \\(exception)\")\n    return\n}\n```\n\nIf the closure did not throw an exception, the function returns `nil`. Otherwise, it returns an instance of `NSException` which you can verify is the exception you expected your block to throw.\n\n \n## Asynchronous Testing\n \nXCTest provides asynchronous testing capabilities using `expectation(description:)` and `waitForExpectations(timeout:handler:)`. However, when testing simple delayed asynchronous actions, this approach can be cumbersome and the intent might not be immediately obvious. Using MetovaTestKit's `MTKWaitThenContinueTest(after:)` utility method, these kinds of tests become simple and they read naturally.\n \n```swift\nmockUserSearchNetworkRequest(withResponseTime: 0.5)\ntestViewController.didTapSearchButton()\n \nXCTAssertFalse(testViewController.searchButton.isEnabled, \"The search button should be disabled while a search request is taking place.\")\n \nMTKWaitThenContinueTest(after: 1)\n \nXCTAssertTrue(testViewController.searchButton.isEnabled, \"Once the request is complete, the search button should be re-enabled.\") \n```\n \n-----\n\n# Documentation\n\nDocumentation can be found [here](http://metova.github.io/MetovaTestKit/).\n \n-----\n\n# Credits\n\nMetovaTestKit is owned and maintained by [Metova Inc.](https://metova.com)\n\n[Contributors](https://github.com/Metova/MetovaTestKit/graphs/contributors)\n\nIf you would like to contribute to MetovaTestKit, see our [CONTRIBUTING](CONTRIBUTING.md) guidelines.\n\nMetovaTestKit banner image and other assets provided by Christi Johnson.\n\n-----\n\n# License\n\nMetovaTestKit is available under the MIT license. See the [LICENSE](LICENSE) file for more info.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmetova%2FMetovaTestKit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmetova%2FMetovaTestKit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmetova%2FMetovaTestKit/lists"}