Skip to content

Dummyable is a lightweight utility for easily generating dummy instances of Swift types for testing and prototyping.

Notifications You must be signed in to change notification settings

hainayanda/Dummyable

Repository files navigation

Dummyable

Dummyable is a lightweight utility for easily generating dummy instances of Swift types for testing and prototyping.

GitHub Release Codacy Badge Unit Test SwiftPM Compatible

Requirements

  • Swift 5.9 or higher
  • iOS 15.0 or higher
  • macOS 12.0 or higher
  • tvOS 15.0 or higher
  • watchOS 8.0 or higher
  • visionOS 1.0 or higher
  • Xcode 15 or higher

Installation

Swift Package Manager (Xcode)

To install using Xcode's Swift Package Manager, follow these steps:

  • Go to File > Swift Package > Add Package Dependency
  • Enter the URL: https://github.com/hainayanda/Dummyable.git
  • Choose Up to Next Major for the version rule and set the version to 1.1.1.
  • Click "Next" and wait for the package to be fetched.

Swift Package Manager (Package.swift)

If you prefer using Package.swift, add Dummyable as a dependency in your Package.swift file:

dependencies: [
    .package(url: "https://github.com/hainayanda/Dummyable.git", .upToNextMajor(from: "1.1.1"))
]

Then, include it in your target:

 .target(
    name: "MyModule",
    dependencies: ["Dummyable"]
)

Usage

Creating Dummy Instance

Dummyable provides an easy way to create dummy instances of basic types:

let intDummy = dummy(of: Int.self)
let intDummies = dummy(of: [Int].self, count: 10)

If your type is marked with @Dummyable or has an associated #Dummy macro declaration, you can create a dummy instance by calling the dummy(of:) global function:

let someDummy = dummy(of: SomeMarkedWithDummyable.self)

Enabling Dummy Creation on Protocol

Dummyable allows you to create dummy instances from a protocol by marking it with @Dummyable:

@Dummyable(.struct)
protocol SomeModelProtocol { 
    var title: String { get }
    var description: String { get set }
    // SomeType must be marked with @Dummyable, have #Dummy macro declaration, or provided by a `dummy(of:)` global function
    var someType: SomeType { get }
    init(title: String, description: String)
    func doSomething()
}

If you want to create a class instance, you can do one of the following methods:

// Tell Dummyable to implement the protocol to a dummy class
@Dummyable(.class)
protocol SomeModelProtocol { }

// Tell Dummyable the protocol is an object
@Dummyable
protocol SomeObjectProtocol: AnyObject { }

// Tell Dummyable the protocol is a class instance
@Dummyable
protocol SomeClassProtocol: class { }

Keep in mind that the @Dummyable macro will not work well on protocols with an associated type or an inheritance clause.

Enabling Dummy on a Struct

Dummyable also allows you to create dummy instances from a struct by marking it with @Dummyable:

@Dummyable
struct SomeModel { 
    let title: String
    var description: String
        
    // SomeType must be marked with @Dummyable, have #Dummy macro declaration, or provided by a `dummy(of:)` global function
    let someType: SomeType
    
    func doSomething() { 
        // Do something
    }
}

If you prefer the dummy to be created using some custom initializer, you can mark it with @DummyableInit:

@Dummyable
struct SomeModel { 
    let title: String
    var description: String
        
    // SomeType must be marked with @Dummyable, have #Dummy macro declaration, or provided by a `dummy(of:)` global function
    let someType: SomeType
    
    @DummyableInit
    init(title: String, description: String = "", someType: SomeType) {
        self.title = title
        self.description = description
    }
    
    func doSomething() { 
        // Do something
    }
}

Then it will create a dummy instance using marked init by only providing the parameter that has no default value.

The macro will still work on the struct with generic parameters as long as the mandatory type can be created using the dummy(of:) global function.

Enabling Dummy on a Class

Dummyable also allows you to create dummy instances from a class by marking it with @Dummyable. But since a class might have a superclass, @DummyableInit to make it work:

@Dummyable
class SomeModel { 
    let title: String
    var description: String
    
    let someType: SomeType
    
    @DummyableInit
    init(title: String, description: String, someType: SomeType) {
        self.title = title
        self.description = description
    }
    // SomeType must be marked with @Dummyable, have #Dummy macro declaration, or provided by a `dummy(of:)` global function
}

The macro will still work on class with a generic parameter as long as the mandatory type can be created using the dummy(of:) global function.

Enabling Dummy on an Enum

Dummyable also allows you to create dummy instances from an enum by marking it with @Dummyable:

@Dummyable
enum SomeEnum {
    case one
    case two
    case three
}

It will try to create an instance using the last case, and the one with the least associated value.

If you want to make sure the dummy instance is created with a specific case, you can mark the enum with @DummyableCase:

@Dummyable
enum SomeEnum {
    @DummyableCase case one
    case two
    case three
}

The macro will still work on an enum with a generic parameter as long as the mandatory type can be created using the dummy(of:) global function.

Dummy with no Declaration

If you want to enable dummy creation on a type that you don't have access to the declaration, you can use the #Dummy macro:

#Dummy(SomeType.self) { 
    SomeType()
}

This will be helpful if you have a dependency that you want to mock but don't have access to the declaration. You can also use this to create a dummy instance of Closure, Tuple, or some other type that is not a class, struct, or enum:

#Dummy((() -> (Int, String)).self) {
    { (1, "Hello") }
}

By default, the global dummy(of:) function will have no modifier and act as an internal function. To make it public or fileprivate, use the #PublicDummy or #PrivateDummy macro:

// Or #PrivateDummy for fileprivate global function
#PublicDummy(SomeType.self) { 
    SomeType()
}

If your type has a generic parameter, you can provide the generic parameter using the Generic type as well:

#Dummy(TypeWithGeneric<Generic>.self) {
    TypeWithGeneric()
}

Generic is a real type that implements Hashable, Equatable, Identifiable, Sendable, Error, and Codable. You can implement more if you need to. But in case you can't, you can use any generic parameter and tell the #Dummy macro to treat it as a generic parameter:

// Tell #Dummy to treat the generic parameter at 0 as generic
#Dummy(TypeWithGeneric<String>.self, .isGeneric(0)) {
    TypeWithGeneric()
}

If you want to add a where clause to the generic parameter, you can do so by providing the where clause:

#Dummy(TypeWithGeneric<String>.self, .where(0, conform: SomeProtocol.self, (any Equatable).self)) {
    TypeWithGeneric()
}

If the Type is only available in a specific platform, you can provide the platform:

#Dummy(TypeWithGeneric<String>.self, .availble(.iOS(15.0))) {
    TypeWithGeneric()
}

How Dummyable Works

Dummyable works by assuming that the mandatory type needed can be created using the dummy(of:) global function:

// On the generated code
self.title = dummy(of: String.self)

So it will throw a compile-time error if the type can't be created using the dummy(of:) global function:

// On the generated code
self.someType = dummy(of: SomeType.self) // Default argument value of type 'Any' cannot be converted to type 'SomeType'

To fix this, you need to mark the type with @Dummyable or provide a #Dummy macro declaration. Or at least manually create a global dummy(of:) function that returns the type:

func dummy(of type: SomeType.Type) -> SomeType {
    SomeType()
}

But doing it manually will not provide you with dummy(of:count:) and some other global function that uses the type that is generated by the @Dummyable or #Dummy macro.

Contributing

Contributions are welcome! Please follow the guidelines in the CONTRIBUTING.md file.

License

Dummyable is available under the MIT license. See the LICENSE file for more info.

Credits

This project is maintained by Nayanda Haberty.

About

Dummyable is a lightweight utility for easily generating dummy instances of Swift types for testing and prototyping.

Topics

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages