Skip to content

Commit

Permalink
feat: add swift issue reporting
Browse files Browse the repository at this point in the history
  • Loading branch information
Kolos65 committed Sep 28, 2024
1 parent 52608dc commit c717aaa
Show file tree
Hide file tree
Showing 42 changed files with 572 additions and 772 deletions.
21 changes: 8 additions & 13 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ let devPlugins: [Target.PluginUsage] = [
let devTargets: [Target] = [
.testTarget(
name: "MockableTests",
dependencies: ["MockableTest"],
dependencies: ["Mockable"],
swiftSettings: [.define("MOCKING")],
plugins: devPlugins
),
Expand All @@ -42,24 +42,19 @@ let package = Package(
.library(
name: "Mockable",
targets: ["Mockable"]
),
.library(
name: "MockableTest",
targets: ["MockableTest"]
),
)
],
dependencies: ifDev(add: devDependencies) + [
.package(url: "https://github.com/swiftlang/swift-syntax.git", "509.0.0"..<"511.0.0")
.package(url: "https://github.com/swiftlang/swift-syntax.git", "509.0.0"..<"511.0.0"),
.package(url: "https://github.com/pointfreeco/swift-issue-reporting", .upToNextMajor(from: "1.4.1"))
],
targets: ifDev(add: devTargets) + [
.target(
name: "Mockable",
dependencies: ["MockableMacro"],
plugins: ifDev(add: devPlugins)
),
.target(
name: "MockableTest",
dependencies: ["Mockable"],
dependencies: [
"MockableMacro",
.product(name: "IssueReporting", package: "swift-issue-reporting")
],
plugins: ifDev(add: devPlugins)
),
.macro(
Expand Down
13 changes: 5 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,7 @@ Read **Mockable**'s [documentation](https://kolos65.github.io/Mockable/documenta

The library can be installed using Swift Package Manager.

**Mockable** provides two library products:
* **Mockable**: Core library containing the [`@Mockable`](https://kolos65.github.io/Mockable/documentation/mockable/mockable()) macro.
* **MockableTest**: Testing utilities that depend on the `XCTest` framework (will only link with test targets).

To use the library:
1. Add **Mockable** to all of your targets that contain protocols you want to mock.
2. Add **MockableTest** to your test targets.
Add the **Mockable** target to all targets that contain protocols you want to mock. **Mockable** does not depend on the `XCTest` framework so it can be added to any target.

Read the [installation guide](https://kolos65.github.io/Mockable/documentation/mockable/installation/) of the documentation for more details on how to integrate **Mockable** with your project.

Expand Down Expand Up @@ -124,7 +118,7 @@ protocol ProductService {
```
A mock implementation named `MockProductService` will be generated, that can be used in unit tests like:
```swift
import MockableTest
import Mockable
lazy var productService = MockProductService()
lazy var cartService = CartServiceImpl(productService: productService)
Expand Down Expand Up @@ -247,6 +241,9 @@ when(productService).url(newValue: .value(nil)).performOnSet {

### Verify
You can verify invocations of your mock service using the `verify(_ service:)` clause.

> **Mockable** supports both *XCTest* and *Swift Testing* by using Pointfree's [swift-issue-reporting](https://github.com/pointfreeco/swift-issue-reporting) to dynamically report test failures with the appropriate test framework.

There are three kind of verifications:
* [`called(_:)`](https://kolos65.github.io/Mockable/documentation/mockable/functionverifybuilder/called(_:file:line:)): Asserts invocation count based on the given value.
* [`getCalled(_:)`](https://kolos65.github.io/Mockable/documentation/mockable/propertyverifybuilder/getcalled(_:file:line:)): Available for mutable properties only, asserts property access count.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
//
// EffectBuilder.swift
// Builder.swift
// Mockable
//
// Created by Kolos Foltanyi on 2023. 11. 21..
//

/// Used to specify members of a protocol when building
/// given or when clauses of a mock service.
public protocol EffectBuilder<Service> {
public protocol Builder<Service> {

/// The mock service associated with the Builder.
associatedtype Service: MockableService
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
///
/// This builder is used within the context of a higher-level builder (e.g., an `ActionBuilder`)
/// to specify a desired action to perform when a particular function of a mock service is called.
public struct FunctionActionBuilder<T: MockableService, ParentBuilder: EffectBuilder<T>> {
public struct FunctionActionBuilder<T: MockableService, ParentBuilder: Builder<T>> {

/// Convenient type for the associated service's Member.
public typealias Member = T.Member
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
///
/// This builder is typically used within the context of a higher-level builder (e.g., a `ReturnBuilder`)
/// to specify the desired return value or a return value producer for a particular function of a mock service.
public struct FunctionReturnBuilder<T: MockableService, ParentBuilder: EffectBuilder<T>, ReturnType, ProduceType> {
public struct FunctionReturnBuilder<T: MockableService, ParentBuilder: Builder<T>, ReturnType, ProduceType> {

/// Convenient type for the associated service's Member.
public typealias Member = T.Member
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
///
/// This builder is typically used within the context of a higher-level builder (e.g., a `VerifyBuilder`)
/// to verify the expected number of invocations for a particular function of a mock service.
public struct FunctionVerifyBuilder<T: MockableService, ParentBuilder: AssertionBuilder<T>> {
public struct FunctionVerifyBuilder<T: MockableService, ParentBuilder: Builder<T>> {

/// Convenient type for the associated service's Member.
public typealias Member = T.Member
Expand All @@ -20,35 +20,36 @@ public struct FunctionVerifyBuilder<T: MockableService, ParentBuilder: Assertion
/// The member being verified.
private var member: Member

/// Assertion function to use for verfications.
private var assertion: MockableAssertion

/// Initializes a new instance of `FunctionVerifyBuilder`.
///
/// - Parameters:
/// - mocker: The `Mocker` instance of the associated mock service.
/// - kind: The member being verified.
/// - assertion: The assertion method to use for verfications.
public init(_ mocker: Mocker<T>, kind member: Member, assertion: @escaping MockableAssertion) {
public init(_ mocker: Mocker<T>, kind member: Member) {
self.member = member
self.mocker = mocker
self.assertion = assertion
}

/// Asserts the number of invocations of the specified member using `count`.
///
/// - Parameter count: Specifies the expected invocation count.
/// - Returns: The parent builder, used for chaining additional specifications.
@discardableResult
public func called(_ count: Count, file: StaticString = #file, line: UInt = #line) -> ParentBuilder {
public func called(
_ count: Count,
fileID: StaticString = #fileID,
filePath: StaticString = #filePath,
line: UInt = #line,
column: UInt = #column) -> ParentBuilder {
mocker.verify(
member: member,
count: count,
assertion: assertion,
file: file,
line: line
fileID: fileID,
filePath: filePath,
line: line,
column: column
)
return .init(mocker: mocker, assertion: assertion)
return .init(mocker: mocker)
}

/// Asynchronously waits at most `timeout` interval for a successfuly assertion of
Expand All @@ -61,16 +62,19 @@ public struct FunctionVerifyBuilder<T: MockableService, ParentBuilder: Assertion
@discardableResult
public func calledEventually(_ count: Count,
before timeout: TimeoutDuration = .seconds(1),
file: StaticString = #file,
line: UInt = #line) async -> ParentBuilder {
fileID: StaticString = #fileID,
filePath: StaticString = #filePath,
line: UInt = #line,
column: UInt = #column) async -> ParentBuilder {
await mocker.verify(
member: member,
count: count,
assertion: assertion,
timeout: timeout,
file: file,
line: line
fileID: fileID,
filePath: filePath,
line: line,
column: column
)
return .init(mocker: mocker, assertion: assertion)
return .init(mocker: mocker)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@
///
/// This builder is used within the context of a higher-level builder (e.g., an `ActionBuilder`)
/// to specify a desired action to perform when a particular throwing function of a mock service is called.
public typealias ThrowingFunctionActionBuilder<T: MockableService, ParentBuilder: EffectBuilder<T>>
public typealias ThrowingFunctionActionBuilder<T: MockableService, ParentBuilder: Builder<T>>
= FunctionActionBuilder<T, ParentBuilder>
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
/// to specify the desired return value or a return value producer for a throwing function of a mock service.
public struct ThrowingFunctionReturnBuilder<
T: MockableService,
ParentBuilder: EffectBuilder<T>,
ParentBuilder: Builder<T>,
ReturnType,
ProduceType
> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@
///
/// This builder is typically used within the context of a higher-level builder (e.g., a `VerifyBuilder`)
/// to verify the expected number of invocations for a throwing function of a mock service.
public typealias ThrowingFunctionVerifyBuilder<T: MockableService, ParentBuilder: AssertionBuilder<T>>
public typealias ThrowingFunctionVerifyBuilder<T: MockableService, ParentBuilder: Builder<T>>
= FunctionVerifyBuilder<T, ParentBuilder>
12 changes: 6 additions & 6 deletions Sources/Mockable/Builder/MockableService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ public protocol MockableService {
associatedtype Member: Matchable, CaseIdentifiable

/// A builder responsible for registering return values.
associatedtype ReturnBuilder: EffectBuilder<Self>
associatedtype ReturnBuilder: Builder<Self>
/// A builder responsible for registering side-effects.
associatedtype ActionBuilder: EffectBuilder<Self>
associatedtype ActionBuilder: Builder<Self>
/// A builder responsible for asserting member invocations.
associatedtype VerifyBuilder: AssertionBuilder<Self>
associatedtype VerifyBuilder: Builder<Self>

/// Encapsulates member return values.
typealias Return = MemberReturn<Member>
Expand All @@ -28,11 +28,11 @@ public protocol MockableService {
typealias Action = MemberAction<Member>

/// A builder proxy for specifying return values.
func given() -> ReturnBuilder
var given: ReturnBuilder { get }

/// A builder proxy for specifying actions.
func when() -> ActionBuilder
var when: ActionBuilder { get }

/// The builder proxy for verifying invocations.
func verify(with assertion: @escaping MockableAssertion) -> VerifyBuilder
var verify: VerifyBuilder { get }
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
///
/// This builder is typically used within the context of a higher-level builder (e.g., an `ActionBuilder`)
/// to specify the behavior of the getter and setter of a particular property of a mock.
public struct PropertyActionBuilder<T: MockableService, ParentBuilder: EffectBuilder<T>> {
public struct PropertyActionBuilder<T: MockableService, ParentBuilder: Builder<T>> {

/// Convenient type for the associated service's Member.
public typealias Member = T.Member
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
/// This builder is typically used within the context of a higher-level builder (e.g., a `ReturnBuilder`)
/// to specify the desired return value or a return value producer for the getter
/// of a particular property of a mock.
public struct PropertyReturnBuilder<T: MockableService, ParentBuilder: EffectBuilder<T>, ReturnType> {
public struct PropertyReturnBuilder<T: MockableService, ParentBuilder: Builder<T>, ReturnType> {

/// Convenient type for the associated service's Member.
public typealias Member = T.Member
Expand Down
Loading

0 comments on commit c717aaa

Please sign in to comment.