Code Monkey home page Code Monkey logo

mockgenerator's Introduction

⚠️ This plugin is no longer being actively worked on as the author is no longer writing much Swift. Updating AppCode can cause breaking changes from time to time so feel free to raise an issue or make a pull request and I will try to fix the problem. Thank you everyone for your support!

Swift Mock Generator for AppCode

An AppCode plugin to generate spy, stub and dummy classes automatically.

AppCode plugin generates Swift spy

Looking for the Xcode version?

Install Swift Mock Generator for AppCode

  • Open AppCode
  • Open AppCode → Preferences ⌘,
  • Go to Plugins
  • Click Browse repositories...
  • Search for 'Swift Mock Generator for AppCode'
  • Click Install
  • Restart AppCode

How to create a new Swift test double

You can generate a spy, stub or dummy. See here for more information around test double nomenclature.

  • Create an empty class conforming to one or many protocols and/or a class.
  • With the cursor inside the class declaration, press ⌥↵.
  • Select 'Generate spy|stub|dummy|partial spy'.

How to recreate a Swift test double

If you change the underlying protocol its test double will need to be regenerated.

To regenerate the test double, place the cursor anywhere inside the test double and select ‘Generate spy|stub|dummy’ again.

Features

Feature Supported
Generate and regenerate a spy.
Generate and regenerate a stub.
Generate and regenerate a dummy.
Generate and regenerate a partial spy (forwards invocations to original implementation if required).
Classes and protocols
Generates test doubles conforming to one or many protocols.
Generates test doubles conforming to a class.
Generates test doubles conforming to both classes and protocols.
Recording methods and properties
Captures invocation status of methods.
Captures invocation status of properties.
Records multiple invocations of methods.
Records multiple invocations of properties.
Captures invoked method parameters.
Records multiple invocations of method parameters.
Supports multiple properties in the same declaration.
Stubbing return values and closures
Stubs values for your test doubles to return.
Stubs a default value for return values where possible.
Automatically calls closure parameters with stubbed values.
Initializers
Generates convenience initializers requiring no parameters.
Supports initializers with arguments.
Supports failable initializers.
Supports required initializers.
Throws
Stub an error for your overridden method to throw.
Supports throwing initializers.
Supports throwing closures.
Generics
Generates generic test doubles from protocols with associated types.
Captures invoked generic parameters.
Captures invoked generic return values.
Scope, keywords, and more
Avoids naming clashes from overloaded methods.
Supports parameter type-annotation attributes and inout.
Respects the test double scope and generates public and open methods and properties. ✅(protocols only)
Generate test doubles inheriting from items in 3rd party frameworks. ✅(protocols only)

Usage example

A protocol called Animator that we wish to spy on:

protocol Animator {
  func animate(duration: TimeInterval, animations: () -> (), completion: (Bool) -> ()) -> Bool
}

Create a spy class conforming to a protocol:

class AnimatorSpy: Animator {
}

Generate the spy:

class AnimatorSpy: Animator {

  var invokedAnimate = false
  var invokedAnimateCount = 0
  var invokedAnimateParameters: (duration: TimeInterval, Void)?
  var invokedAnimateParametersList = [(duration: TimeInterval, Void)]()
  var shouldInvokeAnimateAnimations = false
  var stubbedAnimateCompletionResult: (Bool, Void)?
  var stubbedAnimateResult: Bool! = false

  func animate(duration: TimeInterval, animations: () -> (), completion: (Bool) -> ()) -> Bool {
    invokedAnimate = true
    invokedAnimateCount += 1
    invokedAnimateParameters = (duration, ())
    invokedAnimateParametersList.append((duration, ()))
    if shouldInvokeAnimateAnimations {
      animations()
    }
    if let result = stubbedAnimateCompletionResult {
      completion(result.0)
    }
    return stubbedAnimateResult
  }
}

Inject the spy into the class you wish to test:

let animatorSpy = AnimatorSpy()
let object = ObjectToTest(animator: animatorSpy)

Test if animate method was invoked:

func test_spyCanVerifyInvokedMethod() {
  object.myMethod()
  XCTAssertTrue(animatorSpy.invokedAnimate)
}

Test the correct parameter was passed to the animate method:

func test_spyCanVerifyInvokedParameters() {
  object.myMethod()
  XCTAssertEqual(animatorSpy.invokedAnimateParameters?.duration, 5)
}

Test the number of times animate was invoked:

func test_spyCanVerifyInvokedMethodCount() {
  object.myMethod()
  object.myMethod()
  XCTAssertEqual(animatorSpy.invokedAnimateCount, 2)
}

Test the parameters passed into each call of the animate method:

func test_spyCanVerifyMultipleInvokedMethodParameters() {
  object.myMethod()
  object.myMethod()
  XCTAssertEqual(animatorSpy.invokedAnimateParametersList[0].duration, 5)
  XCTAssertEqual(animatorSpy.invokedAnimateParametersList[1].duration, 5)
}

Stub a return value for the animate method:

func test_spyCanReturnAStubbedValue() {
  animatorSpy.stubbedAnimateResult = true
  let result = object.myMethod()
  XCTAssertTrue(result)
}

Stub the value for the completion closure in the animate method:

func test_spyCanCallClosure_withStubbedValue() {
  animatorSpy.stubbedAnimateCompletionResult = (false, ())
  object.myMethod()
  XCTAssertFalse(object.animationDidComplete)
}

Nomenclature

Despite being called a Mock Generator, this plugin actually generates a spy, stub or dummy. The word 'mock', whilst not technically correct, has been used because test doubles such as spies, mocks, and stubs have become commonly known as mocks.

Build

If you would like to try building the plugin yourself follow these steps.

  • Open build.gradle
  • Change intellij.localPath and runIde.ideDirectory to the path of your AppCode app
  • Run ./gradlew runIde

mockgenerator's People

Contributors

codebrewer avatar seanhenry avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

mockgenerator's Issues

Problem to generate code with Appcode 2019.3

Hello guys,

I am having problem to generates the mock on swift 5.1 on Appcode 2019.3:

The error is the follows:

java.lang.NoSuchMethodError: 'java.util.List com.jetbrains.swift.psi.SwiftProtocolDeclaration.getStatementList()'
	at codes.seanhenry.transformer.ProtocolTransformer.visitProtocolDeclaration(ProtocolTransformer.kt:24)
	at com.jetbrains.swift.psi.impl.SwiftProtocolDeclarationGenImpl.accept(SwiftProtocolDeclarationGenImpl.java:21)
	at com.jetbrains.swift.psi.impl.SwiftProtocolDeclarationGenImpl.accept(SwiftProtocolDeclarationGenImpl.java:25)
	at codes.seanhenry.transformer.ProtocolTransformer$Companion.transform(ProtocolTransformer.kt:13)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
	at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1654)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
	at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)
	at codes.seanhenry.intentions.BaseGeneratingIntention.transformProtocols(BaseGeneratingIntention.java:145)
	at codes.seanhenry.intentions.BaseGeneratingIntention.transformMockClass(BaseGeneratingIntention.java:132)
	at codes.seanhenry.intentions.BaseGeneratingIntention.invoke(BaseGeneratingIntention.java:86)
	at com.intellij.codeInsight.intention.PsiElementBaseIntentionAction.invoke(PsiElementBaseIntentionAction.java:41)
	at com.intellij.codeInsight.intention.impl.config.IntentionActionWrapper.invoke(IntentionActionWrapper.java:58)
	at com.intellij.codeInsight.intention.impl.IntentionActionWithTextCaching$MyIntentionAction.invoke(IntentionActionWithTextCaching.java:181)
	at com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler.lambda$invokeIntention$4(ShowIntentionActionsHandler.java:219)
	at com.intellij.openapi.application.WriteAction.run(WriteAction.java:98)
	at com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler.invokeIntention(ShowIntentionActionsHandler.java:221)
	at com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler.lambda$null$2(ShowIntentionActionsHandler.java:196)
	at com.intellij.openapi.application.TransactionGuardImpl.runSyncTransaction(TransactionGuardImpl.java:83)
	at com.intellij.openapi.application.TransactionGuardImpl.submitTransactionAndWait(TransactionGuardImpl.java:149)
	at com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler.lambda$chooseActionAndInvoke$3(ShowIntentionActionsHandler.java:195)
	at com.intellij.openapi.command.impl.CoreCommandProcessor.executeCommand(CoreCommandProcessor.java:220)
	at com.intellij.openapi.command.impl.CoreCommandProcessor.executeCommand(CoreCommandProcessor.java:178)
	at com.intellij.openapi.command.impl.CoreCommandProcessor.executeCommand(CoreCommandProcessor.java:168)
	at com.intellij.openapi.command.impl.CoreCommandProcessor.executeCommand(CoreCommandProcessor.java:154)
	at com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler.chooseActionAndInvoke(ShowIntentionActionsHandler.java:194)
	at com.intellij.codeInsight.intention.impl.IntentionListStep.lambda$applyAction$0(IntentionListStep.java:107)
	at com.intellij.openapi.application.TransactionGuardImpl.performUserActivity(TransactionGuardImpl.java:193)
	at com.intellij.ui.popup.AbstractPopup.lambda$dispose$13(AbstractPopup.java:1434)
	at com.intellij.util.ui.UIUtil.invokeLaterIfNeeded(UIUtil.java:2413)
	at com.intellij.ide.IdeEventQueue.ifFocusEventsInTheQueue(IdeEventQueue.java:177)
	at com.intellij.ide.IdeEventQueue.executeWhenAllFocusEventsLeftTheQueue(IdeEventQueue.java:129)
	at com.intellij.openapi.wm.impl.FocusManagerImpl.doWhenFocusSettlesDown(FocusManagerImpl.java:159)
	at com.intellij.openapi.wm.impl.IdeFocusManagerImpl.doWhenFocusSettlesDown(IdeFocusManagerImpl.java:36)
	at com.intellij.ui.popup.AbstractPopup.dispose(AbstractPopup.java:1430)
	at com.intellij.ui.popup.WizardPopup.dispose(WizardPopup.java:151)
	at com.intellij.ui.popup.list.ListPopupImpl.dispose(ListPopupImpl.java:326)
	at com.intellij.openapi.util.ObjectNode.lambda$execute$0(ObjectNode.java:104)
	at com.intellij.openapi.util.ObjectTree.executeActionWithRecursiveGuard(ObjectTree.java:183)
	at com.intellij.openapi.util.ObjectNode.execute(ObjectNode.java:71)
	at com.intellij.openapi.util.ObjectTree.executeAll(ObjectTree.java:133)
	at com.intellij.openapi.util.Disposer.dispose(Disposer.java:116)
	at com.intellij.openapi.util.Disposer.dispose(Disposer.java:112)
	at com.intellij.ui.popup.WizardPopup.disposeAllParents(WizardPopup.java:252)
	at com.intellij.ui.popup.list.ListPopupImpl.handleNextStep(ListPopupImpl.java:461)
	at com.intellij.ui.popup.list.ListPopupImpl._handleSelect(ListPopupImpl.java:415)
	at com.intellij.ui.popup.list.ListPopupImpl.handleSelect(ListPopupImpl.java:356)
	at com.intellij.ui.popup.list.ListPopupImpl$1.actionPerformed(ListPopupImpl.java:269)
	at com.intellij.ui.popup.WizardPopup.proceedKeyEvent(WizardPopup.java:367)
	at com.intellij.ui.popup.WizardPopup.dispatch(WizardPopup.java:347)
	at com.intellij.ui.popup.PopupDispatcher.dispatchKeyEvent(PopupDispatcher.java:112)
	at com.intellij.ui.popup.PopupDispatcher.dispatch(PopupDispatcher.java:148)
	at com.intellij.ide.IdePopupManager.dispatch(IdePopupManager.java:93)
	at com.intellij.ide.IdeEventQueue._dispatchEvent(IdeEventQueue.java:746)
	at com.intellij.ide.IdeEventQueue.lambda$dispatchEvent$8(IdeEventQueue.java:422)
	at com.intellij.openapi.progress.impl.CoreProgressManager.computePrioritized(CoreProgressManager.java:698)
	at com.intellij.ide.IdeEventQueue.dispatchEvent(IdeEventQueue.java:421)
	at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
	at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)

Thank you so much

Missing intention documentation causes assertion failure to be logged in AppCode 2017.1 and IDE internal error balloon to show in 2017.2 EAP

If I browse Preferences -> Editor -> Intentions in AppCode 2017.1 then the following is logged to idea.log:

2017-06-26 22:41:23,864 [  12756]  ERROR - config.IntentionActionMetaData - Assertion failed: Intention Description Dir URL is null: Generate mock; MockGenerator, PluginClassLoader[codes.seanhenry.mockgenerator, 2] 
java.lang.Throwable
	at com.intellij.openapi.diagnostic.Logger.assertTrue(Logger.java:168)
	at com.intellij.codeInsight.intention.impl.config.IntentionActionMetaData.getDirURL(IntentionActionMetaData.java:109)
	at com.intellij.codeInsight.intention.impl.config.BeforeAfterActionMetaData.getDescription(BeforeAfterActionMetaData.java:151)
	at com.intellij.codeInsight.intention.impl.config.IntentionManagerSettings.a(IntentionManagerSettings.java:164)
	at com.intellij.codeInsight.intention.impl.config.IntentionManagerSettings.registerMetaData(IntentionManagerSettings.java:155)
	at com.intellij.codeInsight.intention.impl.config.IntentionManagerSettings.registerIntentionMetaData(IntentionManagerSettings.java:86)
	at com.intellij.codeInsight.intention.impl.config.IntentionManagerImpl.b(IntentionManagerImpl.java:92)
	at com.intellij.util.concurrency.QueueProcessor.runSafely(QueueProcessor.java:223)
	at com.intellij.util.Alarm$Request.runSafely(Alarm.java:418)
	at com.intellij.util.Alarm$Request.access$700(Alarm.java:343)
	at com.intellij.util.Alarm$Request$1.run(Alarm.java:385)
	at com.intellij.util.Alarm$Request.run(Alarm.java:396)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at com.intellij.util.concurrency.SchedulingWrapper$MyScheduledFutureTask.run(SchedulingWrapper.java:237)
	at com.intellij.util.concurrency.BoundedTaskExecutor$2.run(BoundedTaskExecutor.java:212)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at java.lang.Thread.run(Thread.java:745)
2017-06-26 22:41:23,867 [  12759]  ERROR - config.IntentionActionMetaData - AppCode 2017.1.3  Build #OC-171.4694.27 
2017-06-26 22:41:23,867 [  12759]  ERROR - config.IntentionActionMetaData - JDK: 1.8.0_112-release 
2017-06-26 22:41:23,867 [  12759]  ERROR - config.IntentionActionMetaData - VM: OpenJDK 64-Bit Server VM 
2017-06-26 22:41:23,867 [  12759]  ERROR - config.IntentionActionMetaData - Vendor: JetBrains s.r.o 
2017-06-26 22:41:23,867 [  12759]  ERROR - config.IntentionActionMetaData - OS: Mac OS X 
2017-06-26 22:41:23,867 [  12759]  ERROR - config.IntentionActionMetaData - Last Action:  
2017-06-26 22:41:23,868 [  12760]  ERROR - til.concurrency.QueueProcessor - @NotNull method com/intellij/codeInsight/intention/impl/config/IntentionActionMetaData.getDirURL must not return null 
java.lang.IllegalStateException: @NotNull method com/intellij/codeInsight/intention/impl/config/IntentionActionMetaData.getDirURL must not return null
	at com.intellij.codeInsight.intention.impl.config.IntentionActionMetaData.getDirURL(IntentionActionMetaData.java:111)
	at com.intellij.codeInsight.intention.impl.config.BeforeAfterActionMetaData.getDescription(BeforeAfterActionMetaData.java:151)
	at com.intellij.codeInsight.intention.impl.config.IntentionManagerSettings.a(IntentionManagerSettings.java:164)
	at com.intellij.codeInsight.intention.impl.config.IntentionManagerSettings.registerMetaData(IntentionManagerSettings.java:155)
	at com.intellij.codeInsight.intention.impl.config.IntentionManagerSettings.registerIntentionMetaData(IntentionManagerSettings.java:86)
	at com.intellij.codeInsight.intention.impl.config.IntentionManagerImpl.b(IntentionManagerImpl.java:92)
	at com.intellij.util.concurrency.QueueProcessor.runSafely(QueueProcessor.java:223)
	at com.intellij.util.Alarm$Request.runSafely(Alarm.java:418)
	at com.intellij.util.Alarm$Request.access$700(Alarm.java:343)
	at com.intellij.util.Alarm$Request$1.run(Alarm.java:385)
	at com.intellij.util.Alarm$Request.run(Alarm.java:396)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at com.intellij.util.concurrency.SchedulingWrapper$MyScheduledFutureTask.run(SchedulingWrapper.java:237)
	at com.intellij.util.concurrency.BoundedTaskExecutor$2.run(BoundedTaskExecutor.java:212)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at java.lang.Thread.run(Thread.java:745)

An error is logged in AppCode 2017.2 EAP in the same circumstances and also at 'random' times when simply editing:

2017-06-26 21:13:28,658 [  34581]  ERROR - til.concurrency.QueueProcessor - Intention Description Dir URL is null: Generate mock; MockGenerator [Plugin: codes.seanhenry.mockgenerator] 
com.intellij.diagnostic.PluginException: Intention Description Dir URL is null: Generate mock; MockGenerator [Plugin: codes.seanhenry.mockgenerator]
	at com.intellij.codeInsight.intention.impl.config.IntentionActionMetaData.getDirURL(IntentionActionMetaData.java:114)
	at com.intellij.codeInsight.intention.impl.config.BeforeAfterActionMetaData.getDescription(BeforeAfterActionMetaData.java:151)
	at com.intellij.codeInsight.intention.impl.config.IntentionManagerSettings.a(IntentionManagerSettings.java:156)
	at com.intellij.codeInsight.intention.impl.config.IntentionManagerSettings.registerMetaData(IntentionManagerSettings.java:147)
	at com.intellij.codeInsight.intention.impl.config.IntentionManagerSettings.registerIntentionMetaData(IntentionManagerSettings.java:78)
	at com.intellij.codeInsight.intention.impl.config.IntentionManagerImpl.a(IntentionManagerImpl.java:92)
	at com.intellij.util.concurrency.QueueProcessor.runSafely(QueueProcessor.java:246)
	at com.intellij.util.Alarm$Request.runSafely(Alarm.java:417)
	at com.intellij.util.Alarm$Request.access$700(Alarm.java:344)
	at com.intellij.util.Alarm$Request$1.run(Alarm.java:384)
	at com.intellij.util.Alarm$Request.run(Alarm.java:395)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at com.intellij.util.concurrency.SchedulingWrapper$MyScheduledFutureTask.run(SchedulingWrapper.java:237)
	at com.intellij.util.concurrency.BoundedTaskExecutor$2.run(BoundedTaskExecutor.java:212)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at java.lang.Thread.run(Thread.java:745)
2017-06-26 21:13:28,661 [  34584]  ERROR - til.concurrency.QueueProcessor - AppCode 2017.2 EAP  Build #OC-172.3095.17 
2017-06-26 21:13:28,661 [  34584]  ERROR - til.concurrency.QueueProcessor - JDK: 1.8.0_152-release 
2017-06-26 21:13:28,661 [  34584]  ERROR - til.concurrency.QueueProcessor - VM: OpenJDK 64-Bit Server VM 
2017-06-26 21:13:28,661 [  34584]  ERROR - til.concurrency.QueueProcessor - Vendor: JetBrains s.r.o 
2017-06-26 21:13:28,661 [  34584]  ERROR - til.concurrency.QueueProcessor - OS: Mac OS X 
2017-06-26 21:13:28,661 [  34584]  ERROR - til.concurrency.QueueProcessor - Last Action:  

Additionally in 2017.2 EAP the IDE internal error balloon pops up (this is enabled by default in EAP builds but it might also be because com.intellij.codeInsight.intention.impl.config.IntentionActionMetaData now throws PluginException if documentation for an intention isn't found, a change between the 171 and 172 code branches). If you show the IDE Fatal Error dialog from the balloon then the option to disable the plugin is shown (which is overkill in this particular case since the lack of documentation doesn't prevent the intention from working correctly).

Bash script support

Is there any way to separate the plugin in a bash script/separate binary? AppCode is not free, but I would love to use the plugin either as a separate executable binary or Xcode plugin.

Feature: Support optional defaults in return types

With a function signature as follows, I would expect the var stubbedGetImageInDiskCacheForKeyResult to be of type UIImage? and be equal to nil by default. Currently it is typed to a UIImage!.
func getImageInDiskCacheForKey(_ key: String, scale: CGFloat) -> UIImage?
Thoughts?

Add ability to check if property accessors were invoked

Hello, Sean!
Thank you for the generator. You did an amazing job!

Would it be hard for you to add some missing functionality for mock code generated for properties?

Consider this code and generated mock.

public protocol Album {
    var photoItems: [Photo] { get } 
    func photos() -> [Photo]
}

public final class MockAlbum: Album {
    public var stubbedPhotoItems: [Photo]!
    public var photoItems: [Photo] {
        return stubbedPhotoItems
    }
    public var invokedPhotos = false
    public var invokedPhotosCount = 0
    public var stubbedPhotosResult: [Photo]!
    public func photos() -> [Photo] {
        invokedPhotos = true
        invokedPhotosCount += 1
        return stubbedPhotosResult
    }
}

It would be great that photoItems property also had invokedGet<ProperyName> and invokedGet<ProperyName>Count.

Otherwise, it is not possible to check if the property was invoked.

Accordingly, for { get set } properties it would be amazing to have the same thing for Setter:
invokedSet<ProperyName> and invokedSet<ProperyName>Count and invokedSet<ProperyName>Parameter

Exception in AppCode

Hi!
I am using this plugin on AppCode with this specs:

AppCode 2018.1.2 EAP
Build #OC-181.4668.26, built on April 18, 2018
Subscription is active until April 27, 2018
JRE: 1.8.0_152-release-1136-b29 x86_64
JVM: OpenJDK 64-Bit Server VM by JetBrains s.r.o
macOS 10.12.6

and I am facing an issue when I try to generate a mock.
Here is the stacktrace:

com.jetbrains.swift.psi.SwiftTypeInheritanceClause.getReferenceTypeElementList()Ljava/util/List;
java.lang.NoSuchMethodError: com.jetbrains.swift.psi.SwiftTypeInheritanceClause.getReferenceTypeElementList()Ljava/util/List;
	at codes.seanhenry.util.finder.SwiftTypeItemFinder.getResolvedTypes(SwiftTypeItemFinder.java:74)
	at codes.seanhenry.util.finder.SwiftTypeItemFinder.findItems(SwiftTypeItemFinder.java:34)
	at codes.seanhenry.intentions.MockGeneratingIntention.getProtocolItemFinder(MockGeneratingIntention.java:121)
	at codes.seanhenry.intentions.MockGeneratingIntention.invoke(MockGeneratingIntention.java:80)
	at com.intellij.codeInsight.intention.PsiElementBaseIntentionAction.invoke(PsiElementBaseIntentionAction.java:43)
	at com.intellij.codeInsight.intention.impl.config.IntentionActionWrapper.invoke(IntentionActionWrapper.java:67)
	at com.intellij.codeInsight.intention.impl.IntentionActionWithTextCaching$MyIntentionAction.invoke(IntentionActionWithTextCaching.java:181)
	at com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler.b(ShowIntentionActionsHandler.java:211)
	at com.intellij.openapi.application.WriteAction.run(WriteAction.java:90)
	at com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler.a(ShowIntentionActionsHandler.java:213)
	at com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler.a(ShowIntentionActionsHandler.java:188)
	at com.intellij.openapi.application.TransactionGuardImpl.a(TransactionGuardImpl.java:88)
	at com.intellij.openapi.application.TransactionGuardImpl.submitTransactionAndWait(TransactionGuardImpl.java:153)
	at com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler.b(ShowIntentionActionsHandler.java:187)
	at com.intellij.openapi.command.impl.CoreCommandProcessor.a(CoreCommandProcessor.java:137)
	at com.intellij.openapi.command.impl.CoreCommandProcessor.executeCommand(CoreCommandProcessor.java:95)
	at com.intellij.openapi.command.impl.CoreCommandProcessor.executeCommand(CoreCommandProcessor.java:85)
	at com.intellij.openapi.command.impl.CoreCommandProcessor.executeCommand(CoreCommandProcessor.java:71)
	at com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler.chooseActionAndInvoke(ShowIntentionActionsHandler.java:186)
	at com.intellij.codeInsight.intention.impl.IntentionListStep.d(IntentionListStep.java:293)
	at com.intellij.openapi.application.TransactionGuardImpl.performUserActivity(TransactionGuardImpl.java:195)
	at com.intellij.ui.popup.AbstractPopup.a(AbstractPopup.java:1398)
	at com.intellij.util.ui.UIUtil.invokeLaterIfNeeded(UIUtil.java:2882)
	at com.intellij.ide.IdeEventQueue.a(IdeEventQueue.java:168)
	at com.intellij.ide.IdeEventQueue.executeWhenAllFocusEventsLeftTheQueue(IdeEventQueue.java:132)
	at com.intellij.openapi.wm.impl.FocusManagerImpl.doWhenFocusSettlesDown(FocusManagerImpl.java:190)
	at com.intellij.openapi.wm.impl.IdeFocusManagerImpl.doWhenFocusSettlesDown(IdeFocusManagerImpl.java:56)
	at com.intellij.ui.popup.AbstractPopup.dispose(AbstractPopup.java:1392)
	at com.intellij.ui.popup.WizardPopup.dispose(WizardPopup.java:160)
	at com.intellij.ui.popup.list.ListPopupImpl.dispose(ListPopupImpl.java:307)
	at com.intellij.openapi.util.Disposer$1.execute(Disposer.java:48)
	at com.intellij.openapi.util.Disposer$1.execute(Disposer.java:44)
	at com.intellij.openapi.util.objectTree.ObjectNode$1.execute(ObjectNode.java:138)
	at com.intellij.openapi.util.objectTree.ObjectNode$1.execute(ObjectNode.java:107)
	at com.intellij.openapi.util.objectTree.ObjectTree.executeActionWithRecursiveGuard(ObjectTree.java:169)
	at com.intellij.openapi.util.objectTree.ObjectNode.execute(ObjectNode.java:107)
	at com.intellij.openapi.util.objectTree.ObjectTree.executeAll(ObjectTree.java:144)
	at com.intellij.openapi.util.Disposer.dispose(Disposer.java:129)
	at com.intellij.openapi.util.Disposer.dispose(Disposer.java:125)
	at com.intellij.ui.popup.WizardPopup.disposeAllParents(WizardPopup.java:263)
	at com.intellij.ui.popup.list.ListPopupImpl.handleNextStep(ListPopupImpl.java:442)
	at com.intellij.ui.popup.list.ListPopupImpl.a(ListPopupImpl.java:396)
	at com.intellij.ui.popup.list.ListPopupImpl.handleSelect(ListPopupImpl.java:337)
	at com.intellij.ui.popup.list.ListPopupImpl$1.actionPerformed(ListPopupImpl.java:250)
	at com.intellij.ui.popup.WizardPopup.a(WizardPopup.java:378)
	at com.intellij.ui.popup.WizardPopup.dispatch(WizardPopup.java:358)
	at com.intellij.ui.popup.PopupDispatcher.dispatchKeyEvent(PopupDispatcher.java:126)
	at com.intellij.ui.popup.PopupDispatcher.dispatch(PopupDispatcher.java:160)
	at com.intellij.ide.IdePopupManager.dispatch(IdePopupManager.java:87)
	at com.intellij.ide.IdeEventQueue._dispatchEvent(IdeEventQueue.java:674)
	at com.intellij.ide.IdeEventQueue.dispatchEvent(IdeEventQueue.java:395)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
	at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
	at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)

Can you help me?

Feature: Provide option to select items to mock

Sometimes it is useful to create a mock that stubs some items, but not others. For example, you may be testing a method who calls another method in that class. You may wish to only stub the called method.

This functionality would likely provide a dialog for you to choose which methods to mock.

Please express your interest below if you are interested in this feature.

Reset Spy between runs

If i have multiple tests that use the same instance of mock, i get fails for the number of times a method was invoked for example

Need a reset method that will send the spy back to initial state

Generation fails with some keywords

Hello Sean !

The mock generation fails when the parameter name matches some Swift keywords.

For example:

protocol Delegate {
  func run(for: String)
}

generates this mock :

class Mock: Delegate {

  var invokedRun = false
  var invokedRunCount = 0
  var invokedRunParameters: (for: String, Void)?
  var invokedRunParametersList = [(for: String, Void)]()

  func run(for: String) {
    invokedRun = true
    invokedRunCount += 1
    invokedRunParameters = (
    invokedRunParametersList.append((
  }
}

Crashing When trying to implement spy

java.lang.NoSuchMethodError: com.jetbrains.swift.psi.SwiftOptionalTypeElement.getTypeElement()Lcom/jetbrains/swift/psi/SwiftTypeElement;
at codes.seanhenry.transformer.TypeTransformer.visitOptionalTypeElement(TypeTransformer.kt:42)
at com.jetbrains.swift.psi.impl.SwiftOptionalTypeElementGenImpl.accept(SwiftOptionalTypeElementGenImpl.java:20)
at com.jetbrains.swift.psi.impl.SwiftOptionalTypeElementGenImpl.accept(SwiftOptionalTypeElementGenImpl.java:24)
at codes.seanhenry.transformer.TypeTransformer$Companion.transform(TypeTransformer.kt:12)
at codes.seanhenry.transformer.FunctionTransformer.transformReturnType(FunctionTransformer.kt:61)
at codes.seanhenry.transformer.FunctionTransformer.visitFunctionDeclaration(FunctionTransformer.kt:28)
at com.jetbrains.swift.psi.impl.SwiftFunctionDeclarationGenImpl.accept(SwiftFunctionDeclarationGenImpl.java:21)
at com.jetbrains.swift.psi.impl.SwiftFunctionDeclarationGenImpl.accept(SwiftFunctionDeclarationGenImpl.java:25)
at codes.seanhenry.transformer.FunctionTransformer$Companion.transform(FunctionTransformer.kt:16)
at codes.seanhenry.transformer.ProtocolTransformer.visitProtocolDeclaration(ProtocolTransformer.kt:26)
at com.jetbrains.swift.psi.impl.SwiftProtocolDeclarationGenImpl.accept(SwiftProtocolDeclarationGenImpl.java:21)
at com.jetbrains.swift.psi.impl.SwiftProtocolDeclarationGenImpl.accept(SwiftProtocolDeclarationGenImpl.java:25)
at codes.seanhenry.transformer.ProtocolTransformer$Companion.transform(ProtocolTransformer.kt:13)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1654)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:913)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)
at codes.seanhenry.intentions.BaseGeneratingIntention.transformProtocols(BaseGeneratingIntention.java:145)
at codes.seanhenry.intentions.BaseGeneratingIntention.transformMockClass(BaseGeneratingIntention.java:132)
at codes.seanhenry.intentions.BaseGeneratingIntention.invoke(BaseGeneratingIntention.java:86)
at com.intellij.codeInsight.intention.PsiElementBaseIntentionAction.invoke(PsiElementBaseIntentionAction.java:41)
at com.intellij.codeInsight.intention.impl.config.IntentionActionWrapper.invoke(IntentionActionWrapper.java:63)
at com.intellij.codeInsight.intention.impl.IntentionActionWithTextCaching$MyIntentionAction.invoke(IntentionActionWithTextCaching.java:179)
at com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler.lambda$invokeIntention$4(ShowIntentionActionsHandler.java:214)
at com.intellij.openapi.application.WriteAction.run(WriteAction.java:92)
at com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler.invokeIntention(ShowIntentionActionsHandler.java:216)
at com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler.lambda$null$2(ShowIntentionActionsHandler.java:191)
at com.intellij.openapi.application.TransactionGuardImpl.runSyncTransaction(TransactionGuardImpl.java:82)
at com.intellij.openapi.application.TransactionGuardImpl.submitTransactionAndWait(TransactionGuardImpl.java:148)
at com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler.lambda$chooseActionAndInvoke$3(ShowIntentionActionsHandler.java:190)
at com.intellij.openapi.command.impl.CoreCommandProcessor.executeCommand(CoreCommandProcessor.java:220)
at com.intellij.openapi.command.impl.CoreCommandProcessor.executeCommand(CoreCommandProcessor.java:178)
at com.intellij.openapi.command.impl.CoreCommandProcessor.executeCommand(CoreCommandProcessor.java:168)
at com.intellij.openapi.command.impl.CoreCommandProcessor.executeCommand(CoreCommandProcessor.java:154)
at com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler.chooseActionAndInvoke(ShowIntentionActionsHandler.java:189)
at com.intellij.codeInsight.intention.impl.IntentionListStep.lambda$applyAction$0(IntentionListStep.java:107)
at com.intellij.openapi.application.TransactionGuardImpl.performUserActivity(TransactionGuardImpl.java:192)
at com.intellij.ui.popup.AbstractPopup.lambda$dispose$13(AbstractPopup.java:1430)
at com.intellij.util.ui.UIUtil.invokeLaterIfNeeded(UIUtil.java:2425)
at com.intellij.ide.IdeEventQueue.ifFocusEventsInTheQueue(IdeEventQueue.java:217)
at com.intellij.ide.IdeEventQueue.executeWhenAllFocusEventsLeftTheQueue(IdeEventQueue.java:169)
at com.intellij.openapi.wm.impl.FocusManagerImpl.doWhenFocusSettlesDown(FocusManagerImpl.java:161)
at com.intellij.openapi.wm.impl.IdeFocusManagerImpl.doWhenFocusSettlesDown(IdeFocusManagerImpl.java:58)
at com.intellij.ui.popup.AbstractPopup.dispose(AbstractPopup.java:1426)
at com.intellij.ui.popup.WizardPopup.dispose(WizardPopup.java:162)
at com.intellij.ui.popup.list.ListPopupImpl.dispose(ListPopupImpl.java:318)
at com.intellij.openapi.util.Disposer$1.execute(Disposer.java:47)
at com.intellij.openapi.util.Disposer$1.execute(Disposer.java:43)
at com.intellij.openapi.util.objectTree.ObjectNode$1.execute(ObjectNode.java:135)
at com.intellij.openapi.util.objectTree.ObjectNode$1.execute(ObjectNode.java:104)
at com.intellij.openapi.util.objectTree.ObjectTree.executeActionWithRecursiveGuard(ObjectTree.java:194)
at com.intellij.openapi.util.objectTree.ObjectNode.execute(ObjectNode.java:104)
at com.intellij.openapi.util.objectTree.ObjectTree.executeAll(ObjectTree.java:142)
at com.intellij.openapi.util.Disposer.dispose(Disposer.java:136)
at com.intellij.openapi.util.Disposer.dispose(Disposer.java:132)
at com.intellij.ui.popup.WizardPopup.disposeAllParents(WizardPopup.java:263)
at com.intellij.ui.popup.list.ListPopupImpl.handleNextStep(ListPopupImpl.java:453)
at com.intellij.ui.popup.list.ListPopupImpl._handleSelect(ListPopupImpl.java:407)
at com.intellij.ui.popup.list.ListPopupImpl.handleSelect(ListPopupImpl.java:348)
at com.intellij.ui.popup.list.ListPopupImpl$1.actionPerformed(ListPopupImpl.java:261)
at com.intellij.ui.popup.WizardPopup.proceedKeyEvent(WizardPopup.java:378)
at com.intellij.ui.popup.WizardPopup.dispatch(WizardPopup.java:358)
at com.intellij.ui.popup.PopupDispatcher.dispatchKeyEvent(PopupDispatcher.java:112)
at com.intellij.ui.popup.PopupDispatcher.dispatch(PopupDispatcher.java:148)
at com.intellij.ide.IdePopupManager.dispatch(IdePopupManager.java:93)
at com.intellij.ide.IdeEventQueue._dispatchEvent(IdeEventQueue.java:763)
at com.intellij.ide.IdeEventQueue.lambda$dispatchEvent$8(IdeEventQueue.java:461)
at com.intellij.openapi.progress.impl.CoreProgressManager.computePrioritized(CoreProgressManager.java:704)
at com.intellij.ide.IdeEventQueue.dispatchEvent(IdeEventQueue.java:460)
at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)

Issue with AppCode 2017.2.3 EAP

message : com.jetbrains.swift.psi.SwiftParameter.getParameterTypeAnnotation()Lcom/jetbrains/swift/psi/SwiftParameterTypeAnnotation;

Capturing how many times a method has been invoked

Hi Sean,

This plugin is quite cool.
Given the absence of mocking frameworks for Swift, I have been generating mocks by my own. So, this plugin can come handy.

One possible enhancement:
In my assertions, I usually assert that a particular method (fooBar) has been called a particular number of times.

  • It just makes the test more robust specially for those methods calls which are not idempotent and result in side-effects.
  • Also, at certain times, in case of continuous network calls, one may want to assert that the call was tried atleast n number of times.

Having used Mockito for Java, I quite like these sort of assertions.

What do you think about having a field like numberOfTimesFooBarInvoked: Int alongside invokedFooBar: Bool?

Feature: Support templating to create custom mocks

The current mocks might not be in everyone’s preferred style. Implementing this feature would allow users to specify their own template. The generator would then generate the mock based on that template.

For example, the following template:

var invocations = Invocations()

struct Invocations {
  {{#methods}}
  var {{name}}: [({{#parameters}}{{type}}, {{/parameters}})]()
  {{/methods}}
}

{{#methods}}
{{signature}} {
  invocations.{{name}}.append(({{#parameters}}{{name}}, {{/parameters}}))
}
{{/methods}}

might create the following mock:

var invocations = Invocations()

struct Invocations {
  var myMethod: [(String, Int)]()
}

func myMethod(arg0: String, arg1: Int) {
  invocations.myMethod.append((arg0, arg1))
}

Comment below if you’re interested in this feature.

Does not work 2020.3

Just wondering if anyone else has issues with the latest app code version. The generator options are not coming up at all.

Cheers Thomas

Version Support 2021.1 EAP

Hello,

I've been trying to use the mock generator on AppCode v2021.1 EAP. However, it's not working. When creating a spy the following error occurs:

2021-02-23 10:24:52,305 [96254536]  ERROR - llij.ide.plugins.PluginManager - 'com.jetbrains.swift.psi.SwiftThrowsClause com.jetbrains.swift.psi.SwiftFunctionDeclaration.getThrowsClause()' 
java.lang.NoSuchMethodError: 'com.jetbrains.swift.psi.SwiftThrowsClause com.jetbrains.swift.psi.SwiftFunctionDeclaration.getThrowsClause()'
	at codes.seanhenry.transformer.FunctionTransformer.visitFunctionDeclaration(FunctionTransformer.kt:37)
	at com.jetbrains.swift.psi.impl.SwiftFunctionDeclarationGenImpl.accept(SwiftFunctionDeclarationGenImpl.java:21)
	at com.jetbrains.swift.psi.impl.SwiftFunctionDeclarationGenImpl.accept(SwiftFunctionDeclarationGenImpl.java:26)
	at codes.seanhenry.transformer.FunctionTransformer$Companion.transform(FunctionTransformer.kt:16)
	at codes.seanhenry.transformer.ClassTransformer.visitClassDeclaration(ClassTransformer.kt:28)
	at com.jetbrains.swift.psi.impl.SwiftClassDeclarationGenImpl.accept(SwiftClassDeclarationGenImpl.java:21)
	at com.jetbrains.swift.psi.impl.SwiftClassDeclarationGenImpl.accept(SwiftClassDeclarationGenImpl.java:26)
	at codes.seanhenry.transformer.ClassTransformer$Companion.transform(ClassTransformer.kt:14)
	at codes.seanhenry.intentions.BaseGeneratingIntention.transformSuperclass(BaseGeneratingIntention.java:141)
	at codes.seanhenry.intentions.BaseGeneratingIntention.transformMockClass(BaseGeneratingIntention.java:135)
	at codes.seanhenry.intentions.BaseGeneratingIntention.invoke(BaseGeneratingIntention.java:86)
	at com.intellij.codeInsight.intention.PsiElementBaseIntentionAction.invoke(PsiElementBaseIntentionAction.java:41)
	at com.intellij.codeInsight.intention.impl.config.IntentionActionWrapper.invoke(IntentionActionWrapper.java:66)
	at com.intellij.codeInsight.intention.impl.IntentionActionWithTextCaching$MyIntentionAction.lambda$invoke$0(IntentionActionWithTextCaching.java:209)
	at com.intellij.util.SlowOperations.lambda$allowSlowOperations$0(SlowOperations.java:77)
	at com.intellij.util.SlowOperations.allowSlowOperations(SlowOperations.java:64)
	at com.intellij.util.SlowOperations.allowSlowOperations(SlowOperations.java:76)
	at com.intellij.codeInsight.intention.impl.IntentionActionWithTextCaching$MyIntentionAction.invoke(IntentionActionWithTextCaching.java:209)
	at com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler.lambda$invokeIntention$4(ShowIntentionActionsHandler.java:251)
	at com.intellij.openapi.application.WriteAction.lambda$run$1(WriteAction.java:105)
	at com.intellij.openapi.application.impl.ApplicationImpl.runWriteActionWithClass(ApplicationImpl.java:948)
	at com.intellij.openapi.application.impl.ApplicationImpl.runWriteAction(ApplicationImpl.java:974)
	at com.intellij.openapi.application.WriteAction.run(WriteAction.java:104)
	at com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler.invokeIntention(ShowIntentionActionsHandler.java:251)
	at com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler.lambda$chooseActionAndInvoke$3(ShowIntentionActionsHandler.java:227)
	at com.intellij.openapi.command.impl.CoreCommandProcessor.executeCommand(CoreCommandProcessor.java:215)
	at com.intellij.openapi.command.impl.CoreCommandProcessor.executeCommand(CoreCommandProcessor.java:172)
	at com.intellij.openapi.command.impl.CoreCommandProcessor.executeCommand(CoreCommandProcessor.java:162)
	at com.intellij.openapi.command.impl.CoreCommandProcessor.executeCommand(CoreCommandProcessor.java:148)
	at com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler.chooseActionAndInvoke(ShowIntentionActionsHandler.java:226)
	at com.intellij.codeInsight.intention.impl.IntentionListStep.lambda$applyAction$3(IntentionListStep.java:135)
	at com.intellij.ide.IdeEventQueue.onFocusEvent(IdeEventQueue.java:568)
	at com.intellij.ide.IdeEventQueue.lambda$dispatchEvent$9(IdeEventQueue.java:485)
	at com.intellij.openapi.application.impl.ApplicationImpl.runIntendedWriteActionOnCurrentThread(ApplicationImpl.java:782)
	at com.intellij.ide.IdeEventQueue.dispatchEvent(IdeEventQueue.java:501)
	at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
	at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
	at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
	at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
2021-02-23 10:24:52,306 [96254537]  ERROR - llij.ide.plugins.PluginManager - AppCode 2021.1 EAP  Build #OC-211.5787.4 
2021-02-23 10:24:52,306 [96254537]  ERROR - llij.ide.plugins.PluginManager - JDK: 11.0.10; VM: Dynamic Code Evolution 64-Bit Server VM; Vendor: JetBrains s.r.o. 
2021-02-23 10:24:52,306 [96254537]  ERROR - llij.ide.plugins.PluginManager - OS: Mac OS X 
2021-02-23 10:24:52,306 [96254537]  ERROR - llij.ide.plugins.PluginManager - Plugin to blame: Swift Mock Generator for AppCode version: 17 
2021-02-23 10:24:52,306 [96254537]  ERROR - llij.ide.plugins.PluginManager - Last Action: ShowIntentionActions 

I noticed I'm running on v17 of the mock generator. When installing v18 manually, I get the message that this version is incompatible.

Plugin 'Swift Mock Generator for AppCode' (version '18') is not compatible with the current version of the IDE, because it requires build 203.* or older but the current build is OC-211.5787.4

Thanks in advance, I very much appreciate the plugin. 😄

Skip

Feature: Support mocks inheriting from classes

AppCode mock generator class support.

Please leave a comment if you are interested in this feature.

Tasks

  • Support 1 superclass
  • Ignore structs
  • Ignore constants
  • Ignore static methods/properties
  • Ignore methods in lower scope (internal only)
  • Ignore final classes and methods
  • Recognise type inferred properties

Hierarchy

  • Support super superclasses
  • Ignore NSObject
  • Support mixed inherited class and protocols

Initialiser

  • Support initialiser with arguments
  • Call descendant initialiser with default arguments where possible.
  • Ignore private and file-private initialisers
  • Support multiple initialisers
  • Support required initialisers
  • Support convenience initialisers
  • Support failable initialisers

Generic

  • Support for generic classes

Scope

  • Support public/open/internal
  • Support open in 3rd party

Class structure

  • Support lazy properties
  • Support implicit type properties set by methods
  • Support implicit type properties set by properties
  • Support implicit type properties set by constructor
  • Support implicit type properties set by literal
  • Support class methods/properties
  • Support computed properties
  • Support private(set), internal(set), fileprivate(set)

Extensions

  • Ignore class extensions (cannot yet be overridden)

Feature: add stubbed error for method with throws

It would be really nice to have an ability to stub error for methods which are declared with throws.

Protocol:

func load() throws {}

Mock:

var stubbedLoadError: Error?

func load() throws {
  if let error = stubbedLoadError {
    throw error
  }
}

Can't make it work

Spy does not call real object

A spy is no good if it does not pass the call to the real object
missing call to super.originalMethod in each generated method

Cannot Generator for Pods

Appcode 2019.3
Xcode 10.3 (not sure if you need this)
macOS 10.14.6

I can successfully generate mocks for anything other than classes/protocols/structs etc from a Cocoapod. After choosing a type (eg: dummy) nothing happens. When generating mocks for non-pod sources, its fairly quick. I also tried letting it sit for 4 minutes to see if it was just being slow.

Thanks,
GW

Issue with optional array in parameters

Thanks for this great Plugin !

I noticed an issue in the generated mock when there is an optional array in the parameters.

For example for this protocol:

protocol MyDelegate {
  func method(_ array: [String]?)
}

this is the generated mock

class MockMyDelegate: MyDelegate {

  var invokedMethod = false
  var invokedMethodCount = 0
  var invokedMethodParameters: (array: [String], Void)?
  var invokedMethodParametersList = [(array: [String], Void)]()

  func method(_ array: [String]?) {
    invokedMethod = true
    invokedMethodCount += 1
    invokedMethodParameters = (array, ())
    invokedMethodParametersList.append((array, ()))
  }
}

and it does not compile :(

I fixed it manually like this:

class MockMyDelegate: MyDelegate {

  var invokedMethod = false
  var invokedMethodCount = 0
  var invokedMethodParameters: (array: [String]?, Void)?
  var invokedMethodParametersList = [(array: [String]?, Void)]()

  func method(_ array: [String]?) {
    invokedMethod = true
    invokedMethodCount += 1
    invokedMethodParameters = (array, ())
    invokedMethodParametersList.append((array, ()))
  }
}

Similar names, different arguments = duplicate generation

Version: 7
AppCode: 2017.2
Xcode: 8.3.3

Issue:
After generating a mock there were a lot of repeated generated methods. Below I have the protocol and the generated mock. Apologies for this example being so large, I will add obvious comments on the duplicate methods in the generated mock.

Second issue, the first argument of a method was a tuple and the generated properties had errors. I will add a comment to the example code where this is.

Protocol:

public protocol AAEngineInterface {
    init()
    
    func currentDevice() -> DeviceInfo
    mutating func refreshAvailableDevices() throws
    mutating func availableDevices() throws -> [DeviceInfo]
    mutating func set(device: DeviceID) throws
    
    // engine calls
    func start() throws
    func stop()
    func reset()
    func isRunning() -> Bool
    func printEngine()
    func attach(_ node: AvNode)
    func detach(_ node: AvNode)
    
    // These 2 methods will be what are similar and get duplicated
    func connect(_ srcNode: AvNode, _ destNode: AvNode)
    func connect(_ srcNode: AvNode, _ destNode: AvNode, _ format: AvFormat)
    
    func connect(_ source: (AvNode, AvBus), _ destination: (AvNode, AvBus))
    func connect(_ source: (AvNode, AvBus), _ destination: (AvNode, AvBus), _ format: AvFormat)
    
    func disconnectInput(_ node: AvNode)
    func disconnectOutput(_ node: AvNode)
    func disconnectInput(_ node: AvNode, bus: AvBus)
    func disconnectOutput(_ node: AvNode, bus: AvBus)
    
    func mixerNode() -> AvMixerNode
    func outputUnit() -> CAUnit
    func inputConnectionPoint(_ forNode: AvNode, _ bus: Int) -> AvConnection?
    func outputConnectionPoints(_ forNode: AvNode, _ bus: Int) -> [AvConnection]
}

Generated Mock:

public class AAEngineMock: AAEngineInterface {
    
    public var invokedCurrentDevice = false
    public var invokedCurrentDeviceCount = 0
    public var stubbedCurrentDeviceResult: DeviceInfo!
    
    
    public func currentDevice() -> DeviceInfo {
        invokedCurrentDevice = true
        invokedCurrentDeviceCount += 1
        return stubbedCurrentDeviceResult
    }
    
    
    public var invokedRefreshAvailableDevices = false
    public var invokedRefreshAvailableDevicesCount = 0
    
    
    public func refreshAvailableDevices() {
        invokedRefreshAvailableDevices = true
        invokedRefreshAvailableDevicesCount += 1
    }
    
    
    public var invokedAvailableDevices = false
    public var invokedAvailableDevicesCount = 0
    public var stubbedAvailableDevicesResult: [DeviceInfo]! = []
    
    
    public func availableDevices() -> [DeviceInfo] {
        invokedAvailableDevices = true
        invokedAvailableDevicesCount += 1
        return stubbedAvailableDevicesResult
    }
    
    
    public var invokedSet = false
    public var invokedSetCount = 0
    public var invokedSetParameters: (device: DeviceID, Void)?
    public var invokedSetParametersList = [(device: DeviceID, Void)]()
    
    
    public func set(device: DeviceID) {
        invokedSet = true
        invokedSetCount += 1
        invokedSetParameters = (device, ())
        invokedSetParametersList.append((device, ()))
    }
    
    
    public var invokedStart = false
    public var invokedStartCount = 0
    
    
    public func start() {
        invokedStart = true
        invokedStartCount += 1
    }
    
    
    public var invokedStop = false
    public var invokedStopCount = 0
    
    
    public func stop() {
        invokedStop = true
        invokedStopCount += 1
    }
    
    
    public var invokedReset = false
    public var invokedResetCount = 0
    
    
    public func reset() {
        invokedReset = true
        invokedResetCount += 1
    }
    
    
    public var invokedIsRunning = false
    public var invokedIsRunningCount = 0
    public var stubbedIsRunningResult: Bool! = false
    
    
    public func isRunning() -> Bool {
        invokedIsRunning = true
        invokedIsRunningCount += 1
        return stubbedIsRunningResult
    }
    
    
    public var invokedPrintEngine = false
    public var invokedPrintEngineCount = 0
    
    
    public func printEngine() {
        invokedPrintEngine = true
        invokedPrintEngineCount += 1
    }
    
    
    public var invokedAttach = false
    public var invokedAttachCount = 0
    public var invokedAttachParameters: (node: AvNode, Void)?
    public var invokedAttachParametersList = [(node: AvNode, Void)]()
    
    
    public func attach(_ node: AvNode) {
        invokedAttach = true
        invokedAttachCount += 1
        invokedAttachParameters = (node, ())
        invokedAttachParametersList.append((node, ()))
    }
    
    
    public var invokedDetach = false
    public var invokedDetachCount = 0
    public var invokedDetachParameters: (node: AvNode, Void)?
    public var invokedDetachParametersList = [(node: AvNode, Void)]()
    
    
    public func detach(_ node: AvNode) {
        invokedDetach = true
        invokedDetachCount += 1
        invokedDetachParameters = (node, ())
        invokedDetachParametersList.append((node, ()))
    }
    
    /*********************************************************
      Start of duplicate methods invokedConnectAvNode
    *********************************************************/  
    public var invokedConnectAvNode = false
    public var invokedConnectAvNodeCount = 0
    public var invokedConnectAvNodeParameters: (srcNode: AvNode, destNode: AvNode)?
    public var invokedConnectAvNodeParametersList = [(srcNode: AvNode, destNode: AvNode)]()
    
    
    public func connect(_ srcNode: AvNode, _ destNode: AvNode) {
        invokedConnectAvNode = true
        invokedConnectAvNodeCount += 1
        invokedConnectAvNodeParameters = (srcNode, destNode)
        invokedConnectAvNodeParametersList.append((srcNode, destNode))
    }
    
    
    public var invokedConnectAvNode = false
    public var invokedConnectAvNodeCount = 0
    public var invokedConnectAvNodeParameters: (srcNode: AvNode, destNode: AvNode, format: AvFormat)?
    public var invokedConnectAvNodeParametersList = [(srcNode: AvNode, destNode: AvNode, format: AvFormat)]()
    
    
    public func connect(_ srcNode: AvNode, _ destNode: AvNode, _ format: AvFormat) {
        invokedConnectAvNode = true
        invokedConnectAvNodeCount += 1
        invokedConnectAvNodeParameters = (srcNode, destNode, format)
        invokedConnectAvNodeParametersList.append((srcNode, destNode, format))
    }
    
    
    /**********************************************************************************
     Start of tuple property naming issue. Notice the second method is a duplicate too
    **********************************************************************************/  
    public var invokedConnect(AvNode, AvBus) = false
    public var invokedConnect(AvNode, AvBus)Count = 0
    public var invokedConnect(AvNode, AvBus)Parameters:(source:(AvNode, AvBus), destination:(AvNode, AvBus))?
    public var invokedConnect(AvNode, AvBus)ParametersList = [(source:(AvNode, AvBus), destination:(AvNode, AvBus))]()
    
    
    public func connect(_ source: (AvNode, AvBus), _ destination: (AvNode, AvBus)) {
        invokedConnect(AvNode, AvBus) = true
        invokedConnect(AvNode, AvBus)
        invokedConnect(AvNode, AvBus)
        invokedConnect(AvNode, AvBus)
    }
    
    
    public var invokedConnect(AvNode, AvBus) = false
    public var invokedConnect(AvNode, AvBus)Count = 0
    public var invokedConnect(AvNode, AvBus)Parameters:(source:(AvNode, AvBus), destination:(AvNode, AvBus), format:AvFormat)?
    public var invokedConnect(AvNode, AvBus)ParametersList = [(source:(AvNode, AvBus), destination:(AvNode, AvBus), format:AvFormat)]()
    
    
    public func connect(_ source: (AvNode, AvBus), _ destination: (AvNode, AvBus), _ format: AvFormat) {
        invokedConnect(AvNode, AvBus) = true
        invokedConnect(AvNode, AvBus)
        invokedConnect(AvNode, AvBus)
        invokedConnect(AvNode, AvBus)
    }
    
    
    public var invokedDisconnectInput = false
    public var invokedDisconnectInputCount = 0
    public var invokedDisconnectInputParameters: (node: AvNode, Void)?
    public var invokedDisconnectInputParametersList = [(node: AvNode, Void)]()
    
    
    public func disconnectInput(_ node: AvNode) {
        invokedDisconnectInput = true
        invokedDisconnectInputCount += 1
        invokedDisconnectInputParameters = (node, ())
        invokedDisconnectInputParametersList.append((node, ()))
    }
    
    
    public var invokedDisconnectOutput = false
    public var invokedDisconnectOutputCount = 0
    public var invokedDisconnectOutputParameters: (node: AvNode, Void)?
    public var invokedDisconnectOutputParametersList = [(node: AvNode, Void)]()
    
    
    public func disconnectOutput(_ node: AvNode) {
        invokedDisconnectOutput = true
        invokedDisconnectOutputCount += 1
        invokedDisconnectOutputParameters = (node, ())
        invokedDisconnectOutputParametersList.append((node, ()))
    }
    
    
    public var invokedDisconnectInputBus = false
    public var invokedDisconnectInputBusCount = 0
    public var invokedDisconnectInputBusParameters: (node: AvNode, bus: AvBus)?
    public var invokedDisconnectInputBusParametersList = [(node: AvNode, bus: AvBus)]()
    
    
    public func disconnectInput(_ node: AvNode, bus: AvBus) {
        invokedDisconnectInputBus = true
        invokedDisconnectInputBusCount += 1
        invokedDisconnectInputBusParameters = (node, bus)
        invokedDisconnectInputBusParametersList.append((node, bus))
    }
    
    
    public var invokedDisconnectOutputBus = false
    public var invokedDisconnectOutputBusCount = 0
    public var invokedDisconnectOutputBusParameters: (node: AvNode, bus: AvBus)?
    public var invokedDisconnectOutputBusParametersList = [(node: AvNode, bus: AvBus)]()
    
    
    public func disconnectOutput(_ node: AvNode, bus: AvBus) {
        invokedDisconnectOutputBus = true
        invokedDisconnectOutputBusCount += 1
        invokedDisconnectOutputBusParameters = (node, bus)
        invokedDisconnectOutputBusParametersList.append((node, bus))
    }
    
    
    public var invokedMixerNode = false
    public var invokedMixerNodeCount = 0
    public var stubbedMixerNodeResult: AvMixerNode!
    
    
    public func mixerNode() -> AvMixerNode {
        invokedMixerNode = true
        invokedMixerNodeCount += 1
        return stubbedMixerNodeResult
    }
    
    
    public var invokedOutputUnit = false
    public var invokedOutputUnitCount = 0
    public var stubbedOutputUnitResult: CAUnit!
    
    
    public func outputUnit() -> CAUnit {
        invokedOutputUnit = true
        invokedOutputUnitCount += 1
        return stubbedOutputUnitResult
    }
    
    
    public var invokedInputConnectionPoint = false
    public var invokedInputConnectionPointCount = 0
    public var invokedInputConnectionPointParameters: (forNode: AvNode, bus: Int)?
    public var invokedInputConnectionPointParametersList = [(forNode: AvNode, bus: Int)]()
    public var stubbedInputConnectionPointResult: AvConnection!
    
    
    public func inputConnectionPoint(_ forNode: AvNode, _ bus: Int) -> AvConnection? {
        invokedInputConnectionPoint = true
        invokedInputConnectionPointCount += 1
        invokedInputConnectionPointParameters = (forNode, bus)
        invokedInputConnectionPointParametersList.append((forNode, bus))
        return stubbedInputConnectionPointResult
    }
    
    
    public var invokedOutputConnectionPoints = false
    public var invokedOutputConnectionPointsCount = 0
    public var invokedOutputConnectionPointsParameters: (forNode: AvNode, bus: Int)?
    public var invokedOutputConnectionPointsParametersList = [(forNode: AvNode, bus: Int)]()
    public var stubbedOutputConnectionPointsResult: [AvConnection]! = []
    
    
    public func outputConnectionPoints(_ forNode: AvNode, _ bus: Int) -> [AvConnection] {
        invokedOutputConnectionPoints = true
        invokedOutputConnectionPointsCount += 1
        invokedOutputConnectionPointsParameters = (forNode, bus)
        invokedOutputConnectionPointsParametersList.append((forNode, bus))
        return stubbedOutputConnectionPointsResult
    }
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.