How can I verify a class method is called using XCTAssert?












0















I have a service class, I would like to assert 2 things




  1. A method is called

  2. The correct params are passed to that method


Here is my class



protocol OAuthServiceProtocol {
func initAuthCodeFlow() -> Void
func renderOAuthWebView(forService service: IdentityEndpoint, queryitems: [String: String]) -> Void
}

class OAuthService: OAuthServiceProtocol {

fileprivate let apiClient: APIClient

init(apiClient: APIClient) {
self.apiClient = apiClient
}

func initAuthCodeFlow() -> Void {

}

func renderOAuthWebView(forService service: IdentityEndpoint, queryitems: [String: String]) -> Void {

}
}


Here are my tests



class OAuthServiceTests: XCTestCase {
var mockAPIClient: APIClient!
var mockURLSession: MockURLSession!
var sut: OAuthService!

override func setUp() {
mockAPIClient = APIClient()
mockAPIClient.session = MockURLSession(data: nil, urlResponse: nil, error: nil)
sut = OAuthService(apiClient: mockAPIClient)
}

func test_InitAuthCodeFlow_CallsRenderOAuthWebView() {
let renderOAuthWebViewExpectation = expectation(description: "RenderOAuthWebView")

class OAuthServiceMock: OAuthService {
override func initAuthCodeFlow() -> Void {

}

override func renderOAuthWebView(forService service: IdentityEndpoint, queryitems: [String: String]) {
renderOAuthWebViewExpectation.fulfill()
}
}
}
}


I was hoping to create a local sub class of OAuthService, assign that as my sut and call something like like sut.initAuthCodeFlow() and then assert that my expectation was fulfilled.



I believe this should satisfy point 1. However I cannot access my expectation when attempting to assign it as fulfilled as I get the following error




Class declaration cannot close over value
'renderOAuthWebViewExpectation' defined in outer scope




How can I mark this as fulfilled?



I am following a TDD approach, so I understand my OAuthService would produce a failing test at this point anyway*










share|improve this question



























    0















    I have a service class, I would like to assert 2 things




    1. A method is called

    2. The correct params are passed to that method


    Here is my class



    protocol OAuthServiceProtocol {
    func initAuthCodeFlow() -> Void
    func renderOAuthWebView(forService service: IdentityEndpoint, queryitems: [String: String]) -> Void
    }

    class OAuthService: OAuthServiceProtocol {

    fileprivate let apiClient: APIClient

    init(apiClient: APIClient) {
    self.apiClient = apiClient
    }

    func initAuthCodeFlow() -> Void {

    }

    func renderOAuthWebView(forService service: IdentityEndpoint, queryitems: [String: String]) -> Void {

    }
    }


    Here are my tests



    class OAuthServiceTests: XCTestCase {
    var mockAPIClient: APIClient!
    var mockURLSession: MockURLSession!
    var sut: OAuthService!

    override func setUp() {
    mockAPIClient = APIClient()
    mockAPIClient.session = MockURLSession(data: nil, urlResponse: nil, error: nil)
    sut = OAuthService(apiClient: mockAPIClient)
    }

    func test_InitAuthCodeFlow_CallsRenderOAuthWebView() {
    let renderOAuthWebViewExpectation = expectation(description: "RenderOAuthWebView")

    class OAuthServiceMock: OAuthService {
    override func initAuthCodeFlow() -> Void {

    }

    override func renderOAuthWebView(forService service: IdentityEndpoint, queryitems: [String: String]) {
    renderOAuthWebViewExpectation.fulfill()
    }
    }
    }
    }


    I was hoping to create a local sub class of OAuthService, assign that as my sut and call something like like sut.initAuthCodeFlow() and then assert that my expectation was fulfilled.



    I believe this should satisfy point 1. However I cannot access my expectation when attempting to assign it as fulfilled as I get the following error




    Class declaration cannot close over value
    'renderOAuthWebViewExpectation' defined in outer scope




    How can I mark this as fulfilled?



    I am following a TDD approach, so I understand my OAuthService would produce a failing test at this point anyway*










    share|improve this question

























      0












      0








      0








      I have a service class, I would like to assert 2 things




      1. A method is called

      2. The correct params are passed to that method


      Here is my class



      protocol OAuthServiceProtocol {
      func initAuthCodeFlow() -> Void
      func renderOAuthWebView(forService service: IdentityEndpoint, queryitems: [String: String]) -> Void
      }

      class OAuthService: OAuthServiceProtocol {

      fileprivate let apiClient: APIClient

      init(apiClient: APIClient) {
      self.apiClient = apiClient
      }

      func initAuthCodeFlow() -> Void {

      }

      func renderOAuthWebView(forService service: IdentityEndpoint, queryitems: [String: String]) -> Void {

      }
      }


      Here are my tests



      class OAuthServiceTests: XCTestCase {
      var mockAPIClient: APIClient!
      var mockURLSession: MockURLSession!
      var sut: OAuthService!

      override func setUp() {
      mockAPIClient = APIClient()
      mockAPIClient.session = MockURLSession(data: nil, urlResponse: nil, error: nil)
      sut = OAuthService(apiClient: mockAPIClient)
      }

      func test_InitAuthCodeFlow_CallsRenderOAuthWebView() {
      let renderOAuthWebViewExpectation = expectation(description: "RenderOAuthWebView")

      class OAuthServiceMock: OAuthService {
      override func initAuthCodeFlow() -> Void {

      }

      override func renderOAuthWebView(forService service: IdentityEndpoint, queryitems: [String: String]) {
      renderOAuthWebViewExpectation.fulfill()
      }
      }
      }
      }


      I was hoping to create a local sub class of OAuthService, assign that as my sut and call something like like sut.initAuthCodeFlow() and then assert that my expectation was fulfilled.



      I believe this should satisfy point 1. However I cannot access my expectation when attempting to assign it as fulfilled as I get the following error




      Class declaration cannot close over value
      'renderOAuthWebViewExpectation' defined in outer scope




      How can I mark this as fulfilled?



      I am following a TDD approach, so I understand my OAuthService would produce a failing test at this point anyway*










      share|improve this question














      I have a service class, I would like to assert 2 things




      1. A method is called

      2. The correct params are passed to that method


      Here is my class



      protocol OAuthServiceProtocol {
      func initAuthCodeFlow() -> Void
      func renderOAuthWebView(forService service: IdentityEndpoint, queryitems: [String: String]) -> Void
      }

      class OAuthService: OAuthServiceProtocol {

      fileprivate let apiClient: APIClient

      init(apiClient: APIClient) {
      self.apiClient = apiClient
      }

      func initAuthCodeFlow() -> Void {

      }

      func renderOAuthWebView(forService service: IdentityEndpoint, queryitems: [String: String]) -> Void {

      }
      }


      Here are my tests



      class OAuthServiceTests: XCTestCase {
      var mockAPIClient: APIClient!
      var mockURLSession: MockURLSession!
      var sut: OAuthService!

      override func setUp() {
      mockAPIClient = APIClient()
      mockAPIClient.session = MockURLSession(data: nil, urlResponse: nil, error: nil)
      sut = OAuthService(apiClient: mockAPIClient)
      }

      func test_InitAuthCodeFlow_CallsRenderOAuthWebView() {
      let renderOAuthWebViewExpectation = expectation(description: "RenderOAuthWebView")

      class OAuthServiceMock: OAuthService {
      override func initAuthCodeFlow() -> Void {

      }

      override func renderOAuthWebView(forService service: IdentityEndpoint, queryitems: [String: String]) {
      renderOAuthWebViewExpectation.fulfill()
      }
      }
      }
      }


      I was hoping to create a local sub class of OAuthService, assign that as my sut and call something like like sut.initAuthCodeFlow() and then assert that my expectation was fulfilled.



      I believe this should satisfy point 1. However I cannot access my expectation when attempting to assign it as fulfilled as I get the following error




      Class declaration cannot close over value
      'renderOAuthWebViewExpectation' defined in outer scope




      How can I mark this as fulfilled?



      I am following a TDD approach, so I understand my OAuthService would produce a failing test at this point anyway*







      swift unit-testing tdd xctestcase






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Jan 1 at 8:54









      Harry BlueHarry Blue

      777824




      777824
























          2 Answers
          2






          active

          oldest

          votes


















          1














          Create a property on your mock, mutating it's value within the method you expect to call. You can then use your XCTAssertEqual to check that prop has been updated.



             func test_InitAuthCodeFlow_CallsRenderOAuthWebView() {
          let renderOAuthWebViewExpectation = expectation(description: "RenderOAuthWebView")

          class OAuthServiceMock: OAuthService {
          var renderOAuthWebViewExpectation: XCTestExpectation!
          var didCallRenderOAuthWebView = false

          override func renderOAuthWebView(forService service: IdentityEndpoint, queryitems: [String: String]) {
          didCallRenderOAuthWebView = true
          renderOAuthWebViewExpectation.fulfill()
          }
          }

          let sut = OAuthServiceMock(apiClient: mockAPIClient)

          XCTAssertEqual(sut.didCallRenderOAuthWebView, false)
          sut.renderOAuthWebViewExpectation = renderOAuthWebViewExpectation

          sut.initAuthCodeFlow()
          waitForExpectations(timeout: 1) { _ in
          XCTAssertEqual(sut.didCallRenderOAuthWebView, true)
          }

          }





          share|improve this answer































            1















            I was hoping to create a local sub class of OAuthService, assign that as my sut and call something like like sut.initAuthCodeFlow() and then assert that my expectation was fulfilled.




            I would strongly discourage you from using this approach. If your SUT is an instance of the subclass then your test is not truly testing OAuthService, but OAuthService mock.



            Moreover, if we think of tests as a tool to:




            • prevent bugs when code is change

            • help refactoring and maintenance of the code


            then I would argue that testing that calling a certain function calls another function is not a good test. That's harsh, I know, so let me unpack why that's the case.



            The only thing it's testing is that initAuthCodeFlow() calls renderOAuthWebView(forService:, queryitems:) under the hood. It doesn't have any assertion on the actual behaviour of the system under test, on the outputs it produces directly or not. If I were to edit the implementation of renderOAuthWebView(forService:, queryitems:) and add some code that would crash at runtime this test would not fail.



            A test like this doesn't help with keeping the codebase easy to change, because if you want to change the implementation of OAuthService, maybe by adding a parameter to renderOAuthWebView(forService:, queryitems:) or by renaming queryitems into queryItems to match the capitalization, you'll have to update both the production code and the test. In other words, the test will get in your way of refactoring -changing how the code looks without changing how it behaves- without any extra benefit.



            So, how should one test OAuthService in a way that prevents bugs and helps moving fast? The trick is all in testing the behaviour instead of the implementation.



            What should OAuthService do? initAuthCodeFlow() doesn't return any value, so we can check for direct outputs, but we can still check indirect outputs, side effects.



            I'm making a guess here, but I from your test checking that renderOAuthWebView(forService:, queryitems:) I'd and the fact that it gets an APIClient type as input I'd say it'll present some kind of web view for a certain URL, and then make another request to the given APIClient maybe with the OAuth token received from the web view?



            A way to test the interaction with APIClient is to make an assertion for the expected endpoint to be called. You can do it with a tool like OHHTTPStubs or with your a custom test double for URLSession that records the requests it gets and allows you to check them.



            As for the presentation of the web view, you can use the delegate patter for it, and set a test double conforming to the delegate protocol which records whether it's called or not. Or you could test at a higher level and inspect the UIWindow in which the test are running to see if the root view controller is the one with the web view.



            At the end of the day is all a matter of trade offs. The approach you've taken is not wrong, it just optimizes more towards asserting the code implementation rather than its behaviour. I hope that with this answer I showed a different kind of optimization, one biased towards the behaviour. In my experience this style of testing proves more helpful in the medium-long run.






            share|improve this answer























              Your Answer






              StackExchange.ifUsing("editor", function () {
              StackExchange.using("externalEditor", function () {
              StackExchange.using("snippets", function () {
              StackExchange.snippets.init();
              });
              });
              }, "code-snippets");

              StackExchange.ready(function() {
              var channelOptions = {
              tags: "".split(" "),
              id: "1"
              };
              initTagRenderer("".split(" "), "".split(" "), channelOptions);

              StackExchange.using("externalEditor", function() {
              // Have to fire editor after snippets, if snippets enabled
              if (StackExchange.settings.snippets.snippetsEnabled) {
              StackExchange.using("snippets", function() {
              createEditor();
              });
              }
              else {
              createEditor();
              }
              });

              function createEditor() {
              StackExchange.prepareEditor({
              heartbeatType: 'answer',
              autoActivateHeartbeat: false,
              convertImagesToLinks: true,
              noModals: true,
              showLowRepImageUploadWarning: true,
              reputationToPostImages: 10,
              bindNavPrevention: true,
              postfix: "",
              imageUploader: {
              brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
              contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
              allowUrls: true
              },
              onDemand: true,
              discardSelector: ".discard-answer"
              ,immediatelyShowMarkdownHelp:true
              });


              }
              });














              draft saved

              draft discarded


















              StackExchange.ready(
              function () {
              StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53994172%2fhow-can-i-verify-a-class-method-is-called-using-xctassert%23new-answer', 'question_page');
              }
              );

              Post as a guest















              Required, but never shown

























              2 Answers
              2






              active

              oldest

              votes








              2 Answers
              2






              active

              oldest

              votes









              active

              oldest

              votes






              active

              oldest

              votes









              1














              Create a property on your mock, mutating it's value within the method you expect to call. You can then use your XCTAssertEqual to check that prop has been updated.



                 func test_InitAuthCodeFlow_CallsRenderOAuthWebView() {
              let renderOAuthWebViewExpectation = expectation(description: "RenderOAuthWebView")

              class OAuthServiceMock: OAuthService {
              var renderOAuthWebViewExpectation: XCTestExpectation!
              var didCallRenderOAuthWebView = false

              override func renderOAuthWebView(forService service: IdentityEndpoint, queryitems: [String: String]) {
              didCallRenderOAuthWebView = true
              renderOAuthWebViewExpectation.fulfill()
              }
              }

              let sut = OAuthServiceMock(apiClient: mockAPIClient)

              XCTAssertEqual(sut.didCallRenderOAuthWebView, false)
              sut.renderOAuthWebViewExpectation = renderOAuthWebViewExpectation

              sut.initAuthCodeFlow()
              waitForExpectations(timeout: 1) { _ in
              XCTAssertEqual(sut.didCallRenderOAuthWebView, true)
              }

              }





              share|improve this answer




























                1














                Create a property on your mock, mutating it's value within the method you expect to call. You can then use your XCTAssertEqual to check that prop has been updated.



                   func test_InitAuthCodeFlow_CallsRenderOAuthWebView() {
                let renderOAuthWebViewExpectation = expectation(description: "RenderOAuthWebView")

                class OAuthServiceMock: OAuthService {
                var renderOAuthWebViewExpectation: XCTestExpectation!
                var didCallRenderOAuthWebView = false

                override func renderOAuthWebView(forService service: IdentityEndpoint, queryitems: [String: String]) {
                didCallRenderOAuthWebView = true
                renderOAuthWebViewExpectation.fulfill()
                }
                }

                let sut = OAuthServiceMock(apiClient: mockAPIClient)

                XCTAssertEqual(sut.didCallRenderOAuthWebView, false)
                sut.renderOAuthWebViewExpectation = renderOAuthWebViewExpectation

                sut.initAuthCodeFlow()
                waitForExpectations(timeout: 1) { _ in
                XCTAssertEqual(sut.didCallRenderOAuthWebView, true)
                }

                }





                share|improve this answer


























                  1












                  1








                  1







                  Create a property on your mock, mutating it's value within the method you expect to call. You can then use your XCTAssertEqual to check that prop has been updated.



                     func test_InitAuthCodeFlow_CallsRenderOAuthWebView() {
                  let renderOAuthWebViewExpectation = expectation(description: "RenderOAuthWebView")

                  class OAuthServiceMock: OAuthService {
                  var renderOAuthWebViewExpectation: XCTestExpectation!
                  var didCallRenderOAuthWebView = false

                  override func renderOAuthWebView(forService service: IdentityEndpoint, queryitems: [String: String]) {
                  didCallRenderOAuthWebView = true
                  renderOAuthWebViewExpectation.fulfill()
                  }
                  }

                  let sut = OAuthServiceMock(apiClient: mockAPIClient)

                  XCTAssertEqual(sut.didCallRenderOAuthWebView, false)
                  sut.renderOAuthWebViewExpectation = renderOAuthWebViewExpectation

                  sut.initAuthCodeFlow()
                  waitForExpectations(timeout: 1) { _ in
                  XCTAssertEqual(sut.didCallRenderOAuthWebView, true)
                  }

                  }





                  share|improve this answer













                  Create a property on your mock, mutating it's value within the method you expect to call. You can then use your XCTAssertEqual to check that prop has been updated.



                     func test_InitAuthCodeFlow_CallsRenderOAuthWebView() {
                  let renderOAuthWebViewExpectation = expectation(description: "RenderOAuthWebView")

                  class OAuthServiceMock: OAuthService {
                  var renderOAuthWebViewExpectation: XCTestExpectation!
                  var didCallRenderOAuthWebView = false

                  override func renderOAuthWebView(forService service: IdentityEndpoint, queryitems: [String: String]) {
                  didCallRenderOAuthWebView = true
                  renderOAuthWebViewExpectation.fulfill()
                  }
                  }

                  let sut = OAuthServiceMock(apiClient: mockAPIClient)

                  XCTAssertEqual(sut.didCallRenderOAuthWebView, false)
                  sut.renderOAuthWebViewExpectation = renderOAuthWebViewExpectation

                  sut.initAuthCodeFlow()
                  waitForExpectations(timeout: 1) { _ in
                  XCTAssertEqual(sut.didCallRenderOAuthWebView, true)
                  }

                  }






                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered Jan 1 at 9:19









                  nodediggitynodediggity

                  74717




                  74717

























                      1















                      I was hoping to create a local sub class of OAuthService, assign that as my sut and call something like like sut.initAuthCodeFlow() and then assert that my expectation was fulfilled.




                      I would strongly discourage you from using this approach. If your SUT is an instance of the subclass then your test is not truly testing OAuthService, but OAuthService mock.



                      Moreover, if we think of tests as a tool to:




                      • prevent bugs when code is change

                      • help refactoring and maintenance of the code


                      then I would argue that testing that calling a certain function calls another function is not a good test. That's harsh, I know, so let me unpack why that's the case.



                      The only thing it's testing is that initAuthCodeFlow() calls renderOAuthWebView(forService:, queryitems:) under the hood. It doesn't have any assertion on the actual behaviour of the system under test, on the outputs it produces directly or not. If I were to edit the implementation of renderOAuthWebView(forService:, queryitems:) and add some code that would crash at runtime this test would not fail.



                      A test like this doesn't help with keeping the codebase easy to change, because if you want to change the implementation of OAuthService, maybe by adding a parameter to renderOAuthWebView(forService:, queryitems:) or by renaming queryitems into queryItems to match the capitalization, you'll have to update both the production code and the test. In other words, the test will get in your way of refactoring -changing how the code looks without changing how it behaves- without any extra benefit.



                      So, how should one test OAuthService in a way that prevents bugs and helps moving fast? The trick is all in testing the behaviour instead of the implementation.



                      What should OAuthService do? initAuthCodeFlow() doesn't return any value, so we can check for direct outputs, but we can still check indirect outputs, side effects.



                      I'm making a guess here, but I from your test checking that renderOAuthWebView(forService:, queryitems:) I'd and the fact that it gets an APIClient type as input I'd say it'll present some kind of web view for a certain URL, and then make another request to the given APIClient maybe with the OAuth token received from the web view?



                      A way to test the interaction with APIClient is to make an assertion for the expected endpoint to be called. You can do it with a tool like OHHTTPStubs or with your a custom test double for URLSession that records the requests it gets and allows you to check them.



                      As for the presentation of the web view, you can use the delegate patter for it, and set a test double conforming to the delegate protocol which records whether it's called or not. Or you could test at a higher level and inspect the UIWindow in which the test are running to see if the root view controller is the one with the web view.



                      At the end of the day is all a matter of trade offs. The approach you've taken is not wrong, it just optimizes more towards asserting the code implementation rather than its behaviour. I hope that with this answer I showed a different kind of optimization, one biased towards the behaviour. In my experience this style of testing proves more helpful in the medium-long run.






                      share|improve this answer




























                        1















                        I was hoping to create a local sub class of OAuthService, assign that as my sut and call something like like sut.initAuthCodeFlow() and then assert that my expectation was fulfilled.




                        I would strongly discourage you from using this approach. If your SUT is an instance of the subclass then your test is not truly testing OAuthService, but OAuthService mock.



                        Moreover, if we think of tests as a tool to:




                        • prevent bugs when code is change

                        • help refactoring and maintenance of the code


                        then I would argue that testing that calling a certain function calls another function is not a good test. That's harsh, I know, so let me unpack why that's the case.



                        The only thing it's testing is that initAuthCodeFlow() calls renderOAuthWebView(forService:, queryitems:) under the hood. It doesn't have any assertion on the actual behaviour of the system under test, on the outputs it produces directly or not. If I were to edit the implementation of renderOAuthWebView(forService:, queryitems:) and add some code that would crash at runtime this test would not fail.



                        A test like this doesn't help with keeping the codebase easy to change, because if you want to change the implementation of OAuthService, maybe by adding a parameter to renderOAuthWebView(forService:, queryitems:) or by renaming queryitems into queryItems to match the capitalization, you'll have to update both the production code and the test. In other words, the test will get in your way of refactoring -changing how the code looks without changing how it behaves- without any extra benefit.



                        So, how should one test OAuthService in a way that prevents bugs and helps moving fast? The trick is all in testing the behaviour instead of the implementation.



                        What should OAuthService do? initAuthCodeFlow() doesn't return any value, so we can check for direct outputs, but we can still check indirect outputs, side effects.



                        I'm making a guess here, but I from your test checking that renderOAuthWebView(forService:, queryitems:) I'd and the fact that it gets an APIClient type as input I'd say it'll present some kind of web view for a certain URL, and then make another request to the given APIClient maybe with the OAuth token received from the web view?



                        A way to test the interaction with APIClient is to make an assertion for the expected endpoint to be called. You can do it with a tool like OHHTTPStubs or with your a custom test double for URLSession that records the requests it gets and allows you to check them.



                        As for the presentation of the web view, you can use the delegate patter for it, and set a test double conforming to the delegate protocol which records whether it's called or not. Or you could test at a higher level and inspect the UIWindow in which the test are running to see if the root view controller is the one with the web view.



                        At the end of the day is all a matter of trade offs. The approach you've taken is not wrong, it just optimizes more towards asserting the code implementation rather than its behaviour. I hope that with this answer I showed a different kind of optimization, one biased towards the behaviour. In my experience this style of testing proves more helpful in the medium-long run.






                        share|improve this answer


























                          1












                          1








                          1








                          I was hoping to create a local sub class of OAuthService, assign that as my sut and call something like like sut.initAuthCodeFlow() and then assert that my expectation was fulfilled.




                          I would strongly discourage you from using this approach. If your SUT is an instance of the subclass then your test is not truly testing OAuthService, but OAuthService mock.



                          Moreover, if we think of tests as a tool to:




                          • prevent bugs when code is change

                          • help refactoring and maintenance of the code


                          then I would argue that testing that calling a certain function calls another function is not a good test. That's harsh, I know, so let me unpack why that's the case.



                          The only thing it's testing is that initAuthCodeFlow() calls renderOAuthWebView(forService:, queryitems:) under the hood. It doesn't have any assertion on the actual behaviour of the system under test, on the outputs it produces directly or not. If I were to edit the implementation of renderOAuthWebView(forService:, queryitems:) and add some code that would crash at runtime this test would not fail.



                          A test like this doesn't help with keeping the codebase easy to change, because if you want to change the implementation of OAuthService, maybe by adding a parameter to renderOAuthWebView(forService:, queryitems:) or by renaming queryitems into queryItems to match the capitalization, you'll have to update both the production code and the test. In other words, the test will get in your way of refactoring -changing how the code looks without changing how it behaves- without any extra benefit.



                          So, how should one test OAuthService in a way that prevents bugs and helps moving fast? The trick is all in testing the behaviour instead of the implementation.



                          What should OAuthService do? initAuthCodeFlow() doesn't return any value, so we can check for direct outputs, but we can still check indirect outputs, side effects.



                          I'm making a guess here, but I from your test checking that renderOAuthWebView(forService:, queryitems:) I'd and the fact that it gets an APIClient type as input I'd say it'll present some kind of web view for a certain URL, and then make another request to the given APIClient maybe with the OAuth token received from the web view?



                          A way to test the interaction with APIClient is to make an assertion for the expected endpoint to be called. You can do it with a tool like OHHTTPStubs or with your a custom test double for URLSession that records the requests it gets and allows you to check them.



                          As for the presentation of the web view, you can use the delegate patter for it, and set a test double conforming to the delegate protocol which records whether it's called or not. Or you could test at a higher level and inspect the UIWindow in which the test are running to see if the root view controller is the one with the web view.



                          At the end of the day is all a matter of trade offs. The approach you've taken is not wrong, it just optimizes more towards asserting the code implementation rather than its behaviour. I hope that with this answer I showed a different kind of optimization, one biased towards the behaviour. In my experience this style of testing proves more helpful in the medium-long run.






                          share|improve this answer














                          I was hoping to create a local sub class of OAuthService, assign that as my sut and call something like like sut.initAuthCodeFlow() and then assert that my expectation was fulfilled.




                          I would strongly discourage you from using this approach. If your SUT is an instance of the subclass then your test is not truly testing OAuthService, but OAuthService mock.



                          Moreover, if we think of tests as a tool to:




                          • prevent bugs when code is change

                          • help refactoring and maintenance of the code


                          then I would argue that testing that calling a certain function calls another function is not a good test. That's harsh, I know, so let me unpack why that's the case.



                          The only thing it's testing is that initAuthCodeFlow() calls renderOAuthWebView(forService:, queryitems:) under the hood. It doesn't have any assertion on the actual behaviour of the system under test, on the outputs it produces directly or not. If I were to edit the implementation of renderOAuthWebView(forService:, queryitems:) and add some code that would crash at runtime this test would not fail.



                          A test like this doesn't help with keeping the codebase easy to change, because if you want to change the implementation of OAuthService, maybe by adding a parameter to renderOAuthWebView(forService:, queryitems:) or by renaming queryitems into queryItems to match the capitalization, you'll have to update both the production code and the test. In other words, the test will get in your way of refactoring -changing how the code looks without changing how it behaves- without any extra benefit.



                          So, how should one test OAuthService in a way that prevents bugs and helps moving fast? The trick is all in testing the behaviour instead of the implementation.



                          What should OAuthService do? initAuthCodeFlow() doesn't return any value, so we can check for direct outputs, but we can still check indirect outputs, side effects.



                          I'm making a guess here, but I from your test checking that renderOAuthWebView(forService:, queryitems:) I'd and the fact that it gets an APIClient type as input I'd say it'll present some kind of web view for a certain URL, and then make another request to the given APIClient maybe with the OAuth token received from the web view?



                          A way to test the interaction with APIClient is to make an assertion for the expected endpoint to be called. You can do it with a tool like OHHTTPStubs or with your a custom test double for URLSession that records the requests it gets and allows you to check them.



                          As for the presentation of the web view, you can use the delegate patter for it, and set a test double conforming to the delegate protocol which records whether it's called or not. Or you could test at a higher level and inspect the UIWindow in which the test are running to see if the root view controller is the one with the web view.



                          At the end of the day is all a matter of trade offs. The approach you've taken is not wrong, it just optimizes more towards asserting the code implementation rather than its behaviour. I hope that with this answer I showed a different kind of optimization, one biased towards the behaviour. In my experience this style of testing proves more helpful in the medium-long run.







                          share|improve this answer












                          share|improve this answer



                          share|improve this answer










                          answered Feb 15 at 10:06









                          mokagiomokagio

                          8,45223243




                          8,45223243






























                              draft saved

                              draft discarded




















































                              Thanks for contributing an answer to Stack Overflow!


                              • Please be sure to answer the question. Provide details and share your research!

                              But avoid



                              • Asking for help, clarification, or responding to other answers.

                              • Making statements based on opinion; back them up with references or personal experience.


                              To learn more, see our tips on writing great answers.




                              draft saved


                              draft discarded














                              StackExchange.ready(
                              function () {
                              StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53994172%2fhow-can-i-verify-a-class-method-is-called-using-xctassert%23new-answer', 'question_page');
                              }
                              );

                              Post as a guest















                              Required, but never shown





















































                              Required, but never shown














                              Required, but never shown












                              Required, but never shown







                              Required, but never shown

































                              Required, but never shown














                              Required, but never shown












                              Required, but never shown







                              Required, but never shown







                              Popular posts from this blog

                              Monofisismo

                              Angular Downloading a file using contenturl with Basic Authentication

                              Olmecas