Skip to content

Commit

Permalink
fix: automockable to support same name on functions and all variable …
Browse files Browse the repository at this point in the history
…types
  • Loading branch information
Mr-T-Dev committed Oct 21, 2024
1 parent 380c3bc commit 51bb99c
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 32 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

# [0.1.1] - 2024-10-21

### Fixed

- Automockable to support functions with same name
- Automackable to support all kind of variables

# [0.1.0] - 2024-10-18

### Added
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ struct MockableFunctionBuilder {
}

private var argumentsRegistration: CodeBlockItemSyntax {
let allParameters = variables.allParameters ?? []
let varName = variables.parametersNameCall

let callVariables: String = variablesName.reduce("", { acc, name in
Expand All @@ -65,6 +66,10 @@ struct MockableFunctionBuilder {
}
})

if allParameters.count == 1 {
return .init(stringLiteral: "self.\(varName).append(\(variablesName.first ?? ""))")
}

return .init(stringLiteral: "self.\(varName).append((\(callVariables)))")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,15 @@ struct MockableVariablesBuilder {
self.accessLevel = accessLevel
}

private var name: String { functionSyntax.name.text }
private var name: String {
let name = functionSyntax.name.text

let params = allParameters ?? []

return params.reduce(name, { acc, param -> String in
return acc+"_\(param.firstName.text)"
})
}

var callsCountName: String { "\(name)CallsCount" }
var calledName: String { "\(name)Called" }
Expand All @@ -40,30 +48,28 @@ struct MockableVariablesBuilder {
)
}

var parametersTupple: String? {
var allParameters: [FunctionParameterSyntax]? {
guard let clause = functionSyntax.signature.parameterClause.as(FunctionParameterClauseSyntax.self) else {
return nil
}

let allParameters: [FunctionParameterSyntax] = clause.parameters.compactMap { $0.as(FunctionParameterSyntax.self) }
return clause.parameters.compactMap { $0.as(FunctionParameterSyntax.self)
}
}

var parametersTupple: String? {
guard let allParameters else { return nil }

var parameterTupple: (FunctionParameterSyntax) -> String = { (param: FunctionParameterSyntax) -> String in
let parameterTupple: (FunctionParameterSyntax) -> String = { (param: FunctionParameterSyntax) -> String in
let name = param.secondName?.text ?? param.firstName.text

if var attributed = param.type.as(AttributedTypeSyntax.self) {
attributed.attributes = .init(stringLiteral: "")
return "\(name): \(attributed.description)"
}

if var identifier = param.type.as(IdentifierTypeSyntax.self) {
return "\(name): \(identifier.description)"
}
let description = param.type.description.replacingOccurrences(of: "@escaping", with: "")

return "\(name): \(param.description)"
return "\(name): \(description)"
}

if allParameters.count == 1 {
return parameterTupple(allParameters[0])
return allParameters[0].type.description
}

return allParameters.reduce("", { acc, param in
Expand All @@ -78,7 +84,16 @@ struct MockableVariablesBuilder {
}

func parametersVar() throws -> VariableDeclSyntax? {
guard let parametersTupple else { return nil }
guard let parametersTupple, let allParameters else { return nil }

if allParameters.count == 1 {
return try .init(
.init(stringLiteral: "\(accessLevel) var \(parametersName): \(parametersTupple)?"),
accessor: {
.init(stringLiteral: "self.\(parametersNameCall).last")
}
)
}

return try .init(
.init(stringLiteral: "\(accessLevel) var \(parametersName): (\(parametersTupple))?"),
Expand All @@ -97,17 +112,22 @@ struct MockableVariablesBuilder {
}

func returnVariable() throws -> VariableDeclSyntax? {
guard let returnValue = functionSyntax.signature.returnClause,
let type = returnValue.type.as(IdentifierTypeSyntax.self) else {
guard let returnValue = functionSyntax.signature.returnClause else {
return nil
}

if type.name.text == "Void" {
if returnValue.description.contains("Void") {
return nil
}

if returnValue.description.contains("?") {
return try .init(
.init(stringLiteral: "\(accessLevel) var \(returnName): \(returnValue.type.description)")
)
}

return try .init(
.init(stringLiteral: "\(accessLevel) var \(returnName): \(type.description)!")
.init(stringLiteral: "\(accessLevel) var \(returnName): \(returnValue.type.description)!")
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,17 @@ import SwiftSyntax
import SwiftSyntaxBuilder
import SwiftSyntaxMacros
import SwiftSyntaxMacrosTestSupport
import SageSwiftKit
import Foundation
import Combine
import XCTest

final class MockableMacrosTests: XCTestCase {
func testMacro() throws {
#if canImport(SageSwiftKitMacros)
assertMacroExpansion(
"""
@AutoMockable
@AutoMockable()
protocol PlayingObject {
var value: String? { get }
Expand All @@ -31,21 +34,21 @@ final class MockableMacrosTests: XCTestCase {
class PlayingObjectMock: PlayingObject {
init() {
}
var tmpFuncCallsCount: Int = 0
var tmpFuncCalled: Bool {
tmpFuncCallsCount > 0
var tmpFunc_valueCallsCount: Int = 0
var tmpFunc_valueCalled: Bool {
tmpFunc_valueCallsCount > 0
}
var tmpFuncParameters: (value: String)? {
self.tmpFuncParametersCalls.last
var tmpFunc_valueParameters: String? {
self.tmpFunc_valueParametersCalls.last
}
var tmpFuncParametersCalls: [(value: String)] = []
var tmpFuncReturnValue: Int!
var tmpFunc_valueParametersCalls: [(String)] = []
var tmpFunc_valueReturnValue: Int!
func tmpFunc(value: String) -> Int {
self.tmpFuncCallsCount += 1
self.tmpFuncParametersCalls.append((value: value))
return self.tmpFuncReturnValue
self.tmpFunc_valueCallsCount += 1
self.tmpFunc_valueParametersCalls.append(value)
return self.tmpFunc_valueReturnValue
}
}
""",
Expand All @@ -55,4 +58,73 @@ final class MockableMacrosTests: XCTestCase {
throw XCTSkip("macros are only supported when running tests for the host platform")
#endif
}

enum TestError: Error {
case noValue
}

@AutoMockable
public protocol ProtocolToTest {
func fetchPeriodsList(
employeeId: Int,
startDate: Date?,
endDate: Date?
) -> AnyPublisher<[Int], TestError>

func fetchDetailPeriod(
employeeId: Int,
period: Double
) -> AnyPublisher<Int, TestError>

func fetchDetailPeriod(
employeeId: Int,
date: Date
) -> AnyPublisher<Double, TestError>

func detailPeriodCache(date: Date) -> TestError?
func fetchCommentsAndChangelog(
employeeId: Int64,
date: Date
) -> AnyPublisher<Int, TestError>

func saveWorkDay(
employeeId: Int64,
date: Date,
from: Date,
to: Date,
breakStart: Date?,
breakEnd: Date?,
breakLength: Int,
comments: [Bool]
) -> AnyPublisher<Int, TestError>
func cancelTimesheet(employeeId: Int64, period: Bool) -> AnyPublisher<Bool, TestError>

func submitTimesheet(
employeeId: Int64,
period: Bool,
overtime: Int?
) -> AnyPublisher<Bool, TestError>

func fetchGeofencingZones(
employeeId: Int64,
cache: Bool
) -> AnyPublisher<Int, TestError>

func fetchCurrentUser() -> AnyPublisher<Int, TestError>

func updatePermission(
employeeId: Int64,
consent: Bool?,
deviceEnable: Bool?
) -> AnyPublisher<Bool, TestError>
}

func testProtocol() {
let mockProtocol: ProtocolToTestMock = ProtocolToTestMock()

XCTAssertEqual(mockProtocol.detailPeriodCache(date: Date()), nil)
mockProtocol.detailPeriodCache_dateReturnValue = .noValue

XCTAssertEqual(mockProtocol.detailPeriodCache(date: Date()), .noValue)
}
}

0 comments on commit 51bb99c

Please sign in to comment.