Tuesday, 11 June 2024

Swift Testing: Simplifying Your Swift Code Testing

 Swift Testing is a package with expressive and intuitive APIs that make testing your Swift code a breeze.

  • Testing Swift code can often be a daunting task, but with Swift Testing, it becomes a breeze. 

  • This package offers expressive and intuitive APIs that make writing and running tests straightforward and efficient. 

  • Whether you're a senior developer or new to Swift, this guide will help you get started with Swift Testing.


Setting Up Your Test Target

To begin, you'll need to set up a new testing target in your Xcode project:

  1. File -> New Target -> Select "Unit Testing Bundle"
  2. In Testing System: Select "Swift Testing"

This setup allows you to integrate Swift Testing into your project seamlessly.





Attributes and Annotations

The @Test Attribute

The cornerstone of Swift Testing is the @Test attribute, which marks functions as tests. Here’s an example:

import Testing

@Test func checkValue() {

    // Test code goes here

}

Key points about @Test:

  1. Recognition: Once you add @Test, Xcode recognizes it and shows a Run icon alongside it.
  2. Functionality: Test functions are ordinary Swift functions annotated with @Test.
  3. Flexibility: They can be global functions or methods in a type, marked as async, throws, or isolated to a global actor such as @MainActor.
  4. Dependencies: To use any other class or struct, you need to import it as shown below:
@testable import className

Macros in Swift Testing

Swift Testing includes two powerful macros: #expect and #require.

#expect Macro

The #expect macro performs expectations and is essential for validating conditions in your tests.

#expect(x == y)

Key features:

  1. Ordinary Expressions: It expects ordinary expressions and language operators.
  2. Detailed Failure: It captures the source code and values of subexpressions if it fails, providing detailed results.

Examples:

#expect(1 == 2)

#expect(user.name == "vishnu")

#expect(!array.isEmpty)

#expect(array.contains(3))




#require Macro

The #require macro is similar to assertions and is used when you want to exit early if an expectation fails.

try #require(session.isValid)
session.isInvalidate()

Key features:

  1. Early Exit: Throws an error if the expression is false, allowing for early exits.
  2. Optional Chaining: Can be used with optional chaining to safely unwrap values.

Example:
let method = try #require(arr.first)
#expect(method.value == 3)


Traits and Test Suites

Traits

Traits allow you to add descriptive names to test methods, improving readability and organization.

@Test("Operators") func checkValue() {

    #expect(1 == 2)

}



Test Suites

A collection of test methods within a type is known as a test suite. Test suites help organize and group related tests.

struct OperatorTests {

    let x = 2

    let y = 2

   @Test("Operators") func checkEqual() {

        #expect(x == y)

    }

    @Test("Operators") func checkSumValue() {

        #expect(x + y == 4)

    }

}



Common Testing Patterns:

1. Test with Conditions
2. Test with Common Charatecterstics
3. Test with different arguments.


Test with Conditions

Organize your tests to check various conditions, ensuring comprehensive coverage.


Test with Common Characteristics

Use tags to group related test cases. This approach enables you to execute specific tests as needed.


We can group all the related test cases in to struct and add a common tag to it.




Test with Different Arguments

Parameterized testing allows you to test a function with multiple sets of arguments without loops. This method automatically tests all elements and provides detailed information if any check fails.




Running Tests in Xcode 16

With Xcode 16, you can run individual arguments by clicking the run button in the test navigator, providing more control over your test execution.



Swift Testing and XCTest:









Conclusion:

 - Swift Testing and XCTest provide robust tools for testing Swift code. 

 - With the expressive and intuitive APIs of Swift Testing, you can write, organize, and run tests more efficiently. 

 - By leveraging attributes like @Test, macros like #expect and #require, and structuring your tests with traits and test suites, you can ensure your code is reliable and bug-free.








Setting Up Multiple App Targets in Xcode from a Single Codebase

 To create two different apps (like "Light" and "Regular") from the same codebase in Xcode, you can follow these steps b...