Code Monkey home page Code Monkey logo

Comments (2)

tiarnann avatar tiarnann commented on May 23, 2024 1

@seanhenry I have a feature idea that solves this and it'd likely be less effort than directly adding an expectation or custom templates.

Problem Description

The problem I'm running into is that I have a ViewModel I'd like to test. Inside the ViewModel there's a function which performs an asynchronous request.

protocol ViewModelDelegate: AnyObject {
      func didFetch(data: Data)
}

class ViewModel {
    let network: Network
    var delegate: ViewModelDelegate?
    
    func performNetworkRequest() {
          network.shared.performRequest({ [delegate] data in
              delegate?.didFetch(data: data)
          })
    }
}

In my test, I'd like to wait until the delegate is called to make an assertion.

class ViewModelTest: XCTestCase {
     var network: MockNetwork!
     var delegate: MockViewModelDelegate!
     var viewModel: ViewModel!

     override func setUp() {
          /*..setup..*/
     }
     
     func test_performNetworkRequest() {
          let testData = Data()
          network.stubbedPerformRequestResult = testData

          viewModel.performNetworkRequest()

          // I want to block here
          
          XCTAssertEqual(delegate.invokedDidFetchParameters.data, testData)
     }
}

Solution/Feature Idea

In addition to generating the invoked/stubbed members, we generate a "perform" member which is a closure accepting the same parameters as the method.

class MockViewModelDelegate: ViewModelDelegate {
    var performDidFetch: ((data: Data) -> Void)?

    func didFetch(data: Data) {
        performDidFetch?(data)
    }
}

This would allow for the caller to set behaviour to occur after the method is called. This could even be the use of an expectation like in Jimmy's case.

func test_performNetworkRequest() {
          let testData = Data()
          network.stubbedPerformRequestResult = testData

          let exp = expectation()
          delegate.performDidFetch = { data in
               XCTAssertEqual(data, testData)
               exp.fulfill()
          }
          
          viewModel.performNetworkRequest()
          waitForExpectations(timeout: 1.0)
     }

This feature is similar to Perform in SwiftyMocky.
https://github.com/MakeAWishFoundation/SwiftyMocky#4-take-action-when-a-stubbed-method-is-called---perform

from swiftmockgeneratorforxcode.

seanhenry avatar seanhenry commented on May 23, 2024

Hi Jimmy,

Thanks for raising this issue and I'm pleased you're finding this tool useful. The templates I'm using are trying to follow the test doubles described in this article and I would like to keep the generated code as simple as possible so that it could be handwritten if required.

However, I think this trick might help. Create an intermediate mock which is generated by this tool. Then extend this class with your mock containing your expectation.

protocol MyProtocol {
    
    func someFunctionThatsCalledInAnimationBlock()
}

class _MyMock: MyProtocol {
    var invokedSomeFunctionThatsCalledInAnimationBlock = false
    var invokedSomeFunctionThatsCalledInAnimationBlockCount = 0
    var someFunctionThatsCalledInAnimationBlockExpectation: XCTestExpectation?
    func someFunctionThatsCalledInAnimationBlock() {
        invokedSomeFunctionThatsCalledInAnimationBlock = true
        invokedSomeFunctionThatsCalledInAnimationBlockCount += 1
        someFunctionThatsCalledInAnimationBlockExpectation?.fulfill()
    }
}

final class MyMock: _MyMock {
    var someFunctionThatsCalledInAnimationBlockExpectation: XCTestExpectation?
    override func someFunctionThatsCalledInAnimationBlock() {
        super. someFunctionThatsCalledInAnimationBlock()
        someFunctionThatsCalledInAnimationBlockExpectation?.fulfill()
    }
}

Whilst this will prevent you from adding the expectation after the mock is regenerated, you will still have to manually write the initial subclass. I have suggested a feature previously which was to enable custom templates. So you could copy an existing template and add your modifications as you wish. If that would solve your problem then we could change this feature request to implement that feature instead? And if it gets enough support then I will build it.

from swiftmockgeneratorforxcode.

Related Issues (20)

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.