|
| 1 | +# ParameterizedTesting |
| 2 | + |
| 3 | + |
| 4 | +[](https://en.wikipedia.org/wiki/MIT_License) |
| 5 | +[](https://codecov.io/gh/cameroncooke/SwiftParameterizedTesting) |
| 6 | + |
| 7 | +ParameterizedTesting is a Swift library for executing parameterized tests using XCTest for iOS. |
| 8 | + |
| 9 | +## What are Parameterized tests? |
| 10 | + |
| 11 | +A parameterized test is a test that runs over and over again using different values. |
| 12 | + |
| 13 | +## Are there specific use-cases in mind? |
| 14 | + |
| 15 | +Yes, this kind of test automation is especially helpful when snapshot testing where you want to ensure you have a snapshot representation for each configuration of a view where they are many permutations, but this can also be used for logic testing. |
| 16 | + |
| 17 | +## Won't I just end up with a single test failing in Xcode if any of the permutations fail? |
| 18 | + |
| 19 | +No, this is where the magic happens, `ParameterizedTesting` will dynamically create individual run-time tests for each permutation so that you know exactly which combination of values failed. When you run the test suite the each test will appear in the Xcode test navigator. |
| 20 | + |
| 21 | +## Any warnings? |
| 22 | + |
| 23 | +Yes, please use this library carefully! It's very easy to end up with 1000s of run-time tests with just a few lines of code. Please be aware that the size of the test suite will grow exponentially for each additional set of values. |
| 24 | + |
| 25 | +```swift |
| 26 | + override class func values() -> ([WeatherData.Weather], [Int]) { |
| 27 | + ( |
| 28 | + [.raining, .sunny, .cloudy, .snowing], |
| 29 | + [12, 34, 3, 22, 0] |
| 30 | + ) |
| 31 | + } |
| 32 | +``` |
| 33 | + |
| 34 | +Above is a simple set of test values, two arrays of 4 and 5 values respectfully. This test alone will generate `4 * 5 = 20` tests. |
| 35 | + |
| 36 | +Now let's look at a larger test dataset: |
| 37 | + |
| 38 | +```swift |
| 39 | + override class func values() -> ( |
| 40 | + [String], |
| 41 | + [Int], |
| 42 | + [String], |
| 43 | + [String], |
| 44 | + [Double], |
| 45 | + [String], |
| 46 | + [String], |
| 47 | + [String], |
| 48 | + [Bool] |
| 49 | + ) { |
| 50 | + ( |
| 51 | + [ |
| 52 | + "raining", |
| 53 | + "sunny", |
| 54 | + "cloudy", |
| 55 | + "snowing", |
| 56 | + ], |
| 57 | + [ |
| 58 | + 12, |
| 59 | + 34, |
| 60 | + 3, |
| 61 | + 22, |
| 62 | + 0, |
| 63 | + ], |
| 64 | + [ |
| 65 | + "apples", |
| 66 | + "oranges", |
| 67 | + ], |
| 68 | + [ |
| 69 | + "red", |
| 70 | + "blue", |
| 71 | + ], |
| 72 | + [ |
| 73 | + 12.99, |
| 74 | + 18.50, |
| 75 | + ], |
| 76 | + [ |
| 77 | + "GB", |
| 78 | + "EU", |
| 79 | + "FR", |
| 80 | + "US", |
| 81 | + ], |
| 82 | + [ |
| 83 | + "large", |
| 84 | + "small", |
| 85 | + ], |
| 86 | + [ |
| 87 | + "red", |
| 88 | + "blue", |
| 89 | + ], |
| 90 | + [ |
| 91 | + true, |
| 92 | + false, |
| 93 | + ] |
| 94 | + ) |
| 95 | + } |
| 96 | +``` |
| 97 | + |
| 98 | +Above is a larger set of test values, 9 arrays of 4, 5, 2, 2, 2, 4, 2, 2, 2 values respectfully. This test will generate `4 * 5 * 2 * 2 * 2 * 4 * 2 * 2 * 2 = 5120` tests! |
| 99 | + |
| 100 | +It's important that you really consider the value of the parameterized tests and use wisely. Even though can test every combination doesn't mean you should and in general you shouldn't. |
| 101 | + |
| 102 | +## Example usage |
| 103 | + |
| 104 | +To use, in your test target create a new Swift file and subclass one of the `ParameterizedTestCase` base classes. Say you want to create test permutations from two sets of data you would use the `ParameterizedTestCase2` base class as shown in the below example, you can use up to 9 datasets in total. Just use corresponding class name i.e. `ParameterizedTestCase9` |
| 105 | + |
| 106 | +```swift |
| 107 | +final class MySnapshotTests: ParameterizedTestCase2<Weather, CelsiusTemperature, Void> { |
| 108 | + override class var defaultTestSuite: XCTestSuite { |
| 109 | + customTestSuite(self) |
| 110 | + } |
| 111 | + |
| 112 | + // MARK: - Internal - |
| 113 | + |
| 114 | + override class func values() -> ([Weather], [Int]) { |
| 115 | + ( |
| 116 | + [.raining, .sunny, .cloudy, .snowing], |
| 117 | + [12, 34, 3, 22, 0] |
| 118 | + ) |
| 119 | + } |
| 120 | + |
| 121 | + override func testAllCombinations( |
| 122 | + _ weather: Weather, |
| 123 | + _ temperature: CelsiusTemperature, |
| 124 | + _ expectedResult: Void? |
| 125 | + ) { |
| 126 | + let view = WeatherView( |
| 127 | + viewModel: WeatherView.ViewModel( |
| 128 | + weather: weather, |
| 129 | + temperature: temperature, |
| 130 | + ) |
| 131 | + ) |
| 132 | + |
| 133 | + assertSnapshot( |
| 134 | + matching: view, |
| 135 | + testName: "\(weather)_\(temperature)" |
| 136 | + ) |
| 137 | + } |
| 138 | +} |
| 139 | +``` |
| 140 | + |
| 141 | +These classes use generics which you must define the types of when defining the class. In the above example the types of each dataset are defined as `<Weather, CelsiusTemperature, Void>`. The Void generic pamemter is a placeholder for an expected value which is only needed when creating logic tests. For snapshot tests it's not needed so here we seet this to void. |
| 142 | + |
| 143 | +It's important that you override `defaultTestSuite` and paste in the following code: |
| 144 | + |
| 145 | +```swift |
| 146 | + override class var defaultTestSuite: XCTestSuite { |
| 147 | + customTestSuite(self) |
| 148 | + } |
| 149 | +``` |
| 150 | + |
| 151 | +This is needed to workaround an issue when creating the run-time tests. |
| 152 | + |
| 153 | +Next just override the `testAllCombinations()` method, this will be autocompleted for you if using Xcode with the parameters already correctly typed. In your method just add the test logic that performs whichever test action you want with the injected values. |
| 154 | + |
| 155 | + |
| 156 | +More fully worked examples including logic tests can be found in [Tests/ExampleTests](Tests/ExampleTests) |
| 157 | + |
| 158 | +## Credits |
| 159 | + |
| 160 | +This library is in part derived from https://github.com/approvals/ApprovalTests.Swift |
| 161 | + |
| 162 | +## License |
| 163 | + |
| 164 | +This library is released under the MIT license. See [LICENSE](LICENSE) for details. |
0 commit comments