-
Notifications
You must be signed in to change notification settings - Fork 4
Sourcery
All packages use Sourcery to generate some mock and tests scenarios.
Use to generate generated mock for protocols.
To add mock for your protocol, you should add the // sourcery: AutoMockable annotation before the definition.
// sourcery: AutoMockable
protocol MyProtocol {
}
It also contains a ResetGeneratedMock used by procotols which contains functions to reset all mock properties.
// sourcery: AutoMockable
protocol MyProtocolGeneratedMock {
func reset() {
XXXCallsCount = 0
XXXReceivedArguments = nil
XXXReceivedInvocations = []
}
}
The mock can genarated subclasses for each associatedtypes contained into your protocol.
Note: the associatedtype MUST be an Equatable. If not, please don't use AutoMockable.
Example of the definition:
// sourcery: AutoMockable
protocol MyProtocol {
associatedtype Other: Equatable
// ...
}
The generated mock:
final class MyProtocolGeneratedMock: MyProtocolGeneratedMock {
// MARK: - Type Alias
typealias Other = OtherMock
// MARK: - Associated Type
struct OtherMock: Equatable {
let id = UUID().uuidString
}
Use to test all functions in your protocol:
- The number of call
- The given parameter(s)
- The return value (if the number of call is greater than 0)
To add generated test for your class, you should add the // sourcery: AutoMockTest annotation before the definition.
// sourcery: AutoMockTest
protocol MyProtocol {
}
If one of the parameters or the return value are not Equatable, you should add an Identical annotation.
Example when the first parameter is Equatable and the second parameter and the return are not Equatable:
// sourcery: AutoMockTest
protocol MyProtocol {
// sourcery: second = "Identical", return = "Identical"
func execute(first: Bool,
second: Second) -> Return
}
Because it's not possible to test with XCTAssertEqual a non Equatable property ! So, in this case, the only way to test property is to using the XCTAssertIdentical.
If we have this mock:
// sourcery: AutoMockTest
protocol MyProtocol {
func execute(first: String,
second: Bool) -> Bool
}
The syntax to test your mock is:
// GIVEN / WHEN
let mock = MyProtocolGeneratedMock()
// THEN
// Test number of calls, parameters and return value
MyProtocolMockTest.XCTAssert(
mock,
expectedNumberOfCalls: 1,
givenFirst: "First"
givenSecond: true
expectedReturnValue: false
)
// OR
// Test only the number of calls
MyProtocolMockTest.XCTCallsCount(
mock,
executeWithFirstAndSecondNumberOfCalls: 1
)
Use to test all @Published variables in your class:
- The number of sinks
- The sink value (if the number of sinks is greater than 0)
To add generated test for your class, you should add the // sourcery: AutoPublisherTest annotation before the definition.
// sourcery: AutoPublisherTest
class MyClass {
}
If your class required an generic type, you must add an annotation with the mock of the generic type.
Note: The key must be in angle brackets (< and >).
// sourcery: AutoMockable
protocol MyProtocol {
}
// sourcery: AutoPublisherTest
// sourcery: <P> = "MyProtocolGeneratedMock"
class MyClass<P: MyProtocol> {
@Published private (set) var myProperty: P?
}
If your @Published value is not an Equatable, you should add an Identical annotation.
Example:
struct NotEquatable {}
// sourcery: AutoPublisherTest
// sourcery: myProperty = "Identical"
class MyClass {
@Published private (set) var myProperty: NotEquatable?
}
Because it's not possible to test with XCTAssertEqual a non Equatable property ! So, in this case, the only way to test property is to using the XCTAssertIdentical.
If we have this class:
// sourcery: AutoPublisherTest
class MyClass {
@Published private (set) var myValue: Int?
}
The syntax to test your mock is:
// GIVEN / WHEN
let myClass = MyClass()
var subscriptions = Set<AnyCancellable>()
let myValuePublisherMock = .init(publisher: myClass.$myValue)
myValuePublisherMock.loadTesting(on: &subscriptions)
// THEN
// Test number of sinks and sink value
MyClassPublisherTest.XCTAssert(
myValue: myValuePublisherMock,
expectedNumberOfSinks: 1,
expectedValue: 11
)
// OR
// Test only the number of sinks
MyClassPublisherTest.XCTSinksCount(
myValue: myValuePublisherMock,
expectedNumberOfSinks: 1
)
Use to create a stub for ViewModel.
The stub can contains dependencies (UseCases, ...) and PublisherMock if the ViewModel has this type of data.
To add generated test for your ViewModel, you should add the // sourcery: AutoViewModelStub annotation before the definition.
// sourcery: AutoViewModelStub
class MyViewModel {
}
If your ViewModel required an generic type, you must add an annotation with the mock of the generic type.
Note: The key must be in angle brackets (< and >).
// sourcery: AutoMockable
protocol MyProtocol {
}
// sourcery: AutoViewModelStub
// sourcery: <P> = "MyProtocolGeneratedMock"
class MyViewModel<P: MyProtocol> {
private let useCase: P
}
If your ViewModel contains at least one @Published property, to load the subscription of the Publisher, you need to call the subscribePublishers with an Set stored on your XCTest:
var subscriptions = Set<AnyCancellable>()
func test_XXX() {
let stub = MyViewModelStub()
stub.subscribePublishers(on: &self.subscriptions)
// ...
}
If you want to reset all mock data for dependencies (number of calls, arguments, return value, ...) or @Published (number of sinks, sink value), you need to call the resetMockedData function:
var subscriptions = Set<AnyCancellable>()
func test_XXX() {
let stub = MyViewModelStub()
stub.resetMockedData()
// ...
}
If we have this UseCase:
// sourcery: AutoMockable
// sourcery: AutoMockTest
protocol MyUseCase {
func execute(first: Int) -> Bool
}
And this ViewModel:
// sourcery: AutoViewModelStub
// sourcery: AutoPublisherTest
class MyViewModel {
@Published private (set) var myValue: Int?
let myUseCase: MyUseCase
}
The final implementation for the stub is:
final class Stub: MyViewModelStub {
init() {
let myUseCaseMock = MyUseCaseGeneratedMock()
myUseCaseMock.executeWithFirstReturnValue = true
let viewModel = MyViewModel(
myUseCase: myUseCaseMock
)
super.init(
viewModel: viewModel,
myUseCaseMock: myUseCaseMock
)
}
}
The syntax to test your mock is:
// GIVEN
var subscriptions = Set<AnyCancellable>()
let stub = Stub()
stub.subscribePublishers(on: &self.subscriptions)
stub.resetMockedData() // You can reset mock after the init of the stub if you want.
// WHEN
// ...
// THEN
// **
// Publisher
MyViewModelPublisherTest.XCTAssert(
myValue: stub.myValuePublisherMock,
expectedNumberOfSinks: 1,
expectedValue: 22
)
// OR
MyViewModelPublisherTest.XCTSinksCount(
myValue: stub.myValuePublisherMock,
expectedNumberOfSinks: 1
)
// **
// **
// UseCase
MyUseCaseMockTest.XCTAssert(
stub.myUseCaseMock,
expectedNumberOfCalls: 1,
expectedReturnValue: true
)
// OR
MyUseCaseMockTest.XCTCallsCount(
stub.myUseCaseMock,
executeWithFirstNumberOfCalls: 1
)
// **