Say we have this enum
enum Action: String {
case doThing
case doOtherThing
}
This enum is used this way:
func run(action: Action, block: () -> Void)
Now, I unit test the run method so I need to pass an Action this way:
func testActionRun() {
let expect = expectation(description: #function)
let sut = ActionRunner()
sut.run(action: .doThing) {
expect.fulfill()
// Assert something
}
waitForExpectations(timeout: 0.1, handler: nil)
}
As I need to test other situations on ActionRunner, I ended with a lot of .doThing spread over the whole test suite.
The problem is: if I make a change in production code and change case doThing to case doThatThing now all my test suite fails because there is no a case doThing.
The perfect thing would be to declare a dummy case in test code to allow something like
sut.run(action: .dummyAction) {
}
but enum does not allow that as it doesn't allows inheritance nor a extension to add a case.
The first option that came to my mind was to convert Action into a protocol, but that change is unnecessary in production and its only purpose is to accomplish something in test code.
So, is it there another option to achieve this?
The question of how to avoid coupling when using enums is a tricky one. I bumped into that myself a few times with no solid answer :/
One point you raise is the one of using a protocol, and that feels unnecessary in production. I sort of agree with that, but most time it's the necessary evil.
In the example you showed though I think maybe a tweak in the design might solve part of the problem.
In particular when looking at this code
func run(action: Action, block: () -> Void) {
// ...
}
func testActionRun() {
let expect = expectation(description: #function)
let sut = ActionRunner()
sut.run(action: .doThing) {
expect.fulfill()
// Assert something
}
waitForExpectations(timeout: 0.1, handler: nil)
}
What comes to mind to me is that your Action specifies a certain behaviour. That is when you test the run method passing .doThing you expect a different behaviour than when passing .doOtherThing.
If that's right, is there any reason why you need to pass the action enum instance and an action block to the run function?
You could separate the code that defines the behaviour from the one performs the actual action even more that what you've done already. For example:
protocol Actionable {
var action: () -> () { get }
}
enum Action: Actionable {
case doThing
case doOtherThing
var action {
switch self {
case .doThing: return ...
case .doOtherThing: return ...
}
}
class ActionRunner {
func run(actionable: Actionable) {
actionable.action()
}
}
func testActionRun() {
let expect = expectation(description: #function)
let sut = ActionRunner()
sut.run(actionable: FakeActionable()) {
expectation.fulfill()
}
waitForExpectations(timeout: 0.1, handler: nil)
}
class FakeActionable: Actionable {
let action = { }
}
func testDoThing() {
let sut = Action.doThing
sut.action()
// XCTAssert for the expected effect of the action
}
Note: I haven't actually compiled that code, so bear with me if it has some mistakes. It should give the idea though.
This way you have ActionRunner which only purpose is to properly run a given Actionable, and the Action enum which only purpose is to describe what different actions should do.
This example code is rather restrict in what it can do, only run () -> () actions, but you could build on top of it to achieve more advanced behaviours.
If you change your production code you have to change your test code too in order to test those new changes.
Maybe you can set the value on an Action variable in the setUp func of your XCTestCase class
import XCTest
class SharingKitTests: XCTestCase {
var theAction: Action!
override func setUp() {
super.setUp()
self.theAction = .doThing
}
}
Then you will be able to use this theAction var in all your test methods, and if you need to change the value you only need to change it in one place.
Related
I need your help, this time for Unit Test and how to evaluate a Singleton into Function. This is my context:
I have a class, which it has a Singleton like this:
class ClassToTest (){
var testMessage = ""
init(){
// This is only a empty init
}
func funcWhichHasASingleton() {
testMessage = "Function was covered"
MySingleton.shared.sendAnalytics("params")
}
}
As you can see, there is a singleton where is sending analytics
MySingleton.shared.sendAnalytics("params")
And I have my test function like this:
func testExample() {
// Given
let sut = ClassToTest()
// When
sut.funcWhichHasASingleton()
// Then
XCTAssertEqual(sut.testMessage, "Function was covered")
}
The my question is: how I can test the Singleton into ClassToTest, such as Xcode pass into that function but my SonarQube says I need to cover the Singleton line. How can I do that?
Breaking this down a little, what you actually need to to test is not the function being called but what the function does. In this case, I think you want to make sure the analytics are being called.
In which case you would need to do something like:
class ClassToTest {
let analytics: MySingleton
init(analytics: MySingleton) {
self.analytics = analytics
}
func funcWhichHasASingleton() {
analytics.sendAnalytics("params")
}
}
Then you are in a position to test the analytics, possibly with a mock.
func testExample() {
let analytics = MySingleton.shared // or better, some kind of Mock class.
// Given
let sut = ClassToTest(analytics: analytics)
// When
sut.funcWhichHasASingleton()
// Then
XCTAssertEqual(analytics.messageCount, 1) // You would need to be able to monitor what the singletons does.
}
Potentially, if you can have a mock MySingleton, then you can do this. Maybe using Mockingbird or use a protocol witness.
let didSendSendExpectation = expectation("")
analytics.didRecieve { message in
didSendSendExpectation.fulfill()
}
// When
sut.funcWhichHasASingleton()
wait(for: didSendSendExpectation, timeout: 0.1)
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 months ago.
Improve this question
struct SubSection {
let title:String
var options: [SubSettingsOptionType]
}
enum SubSettingsOptionType {
case staticCell(model:SubSettingsOption)
case switchCell(model:SubSettingsSwitchOption)
}
class SubSettingsSwitchOption {
let title:String
let handler: (() -> Void)
let isOn:Bool
init(title:String, _ mHandler:#escaping (() -> Void), isOn:Bool) {
self.title = title
self.isOn = isOn
self.handler = mHandler
}
}
class SubSettingsOption {
var title:String
var isSelected:Bool
var handler: ((Int, Int) -> Void)
init(title:String, isSelected:Bool, _ mHandler:#escaping ((Int, Int) -> Void)) {
self.title = title
self.isSelected = isSelected
self.handler = mHandler
}
}
I came across a code with a complex structure for me as above
When I try to access and use this code, I am using the following, but it is very inconvenient every time I use it.
Is there an easier way to access the data?
let type = models[0].options[0]
switch type.self {
case .staticCell(let model):
model.isSelected = true
case .switchCell(let model):
model.handler()
}
If you're stuck with the enum, the switch is probably the most convenient method of doing different things for the various cases. if also supports patterns that can match the cases of an enum, but that's rarely more convenient than a switch, and is usually noticeably more awkward, not to mention that if can't enforce that you handled all cases of an enum.
But that doesn't mean you have to litter your code with switch statements. If what you're doing in the various cases is consistent, extract that into a function, for example create an extension on SubSettingsOptionType in which this function would be a method in that extension, and then just use that function instead of a switch every time.
I'll give you an example, but keep in mind I don't really know anything about the context of your code, so the names I give things almost certainly can be much better.
extension SubSettingsOptionType
{
func apply()
{
switch self
{
case .staticCell(let model):
model.isSelected = true
case .switchCell(let model):
model.handler()
}
}
}
With that in place, the code you find inconvenient can be reduced to:
models[0].options[0].apply()
You may have several sets of use cases that come up repeatedly in your code, so you may end up with several such functions, each to handle one of the consistent sets. If you name the function(s) well, your code will be easier to read too.
Another approach might be to imagine how you'd design it in a more convenient way if you were free to replace the enum with something else, then write a set of adapters that wrap the enum, but provide the API you'd prefer.
For example, maybe you'd prefer polymorphism, you might define something like:
protocol CellOptionType {
func apply()
}
struct StaticCellOptionType: CellOptionType
{
var model: SubSettingsOption
func apply() { model.isSelected = true }
}
struct SwitchCellOptionType: CellOptionType
{
var model: SubSettingsSwitchOption
func apply() { model.handler() }
}
extension SubSettingsOptionType
{
var cellOptionType: CellOptionType
{
switch self
{
case .staticCell(let model):
return StaticCellOptionType(model: model)
case .switchCell(let model):
return SwitchCellOptionType(model: model)
}
}
}
Now instead of your original code you can write
models[0].options[0].cellOptionType.apply()
or assign it to a variable to do something with it later
let cellOptionType = models[0].options[0].cellOptionType
// Some time later in your code
cellOptionType.apply()
Is this actually better? Maybe. It depends on how often you need to call apply and how much you store/pass-around SubSettingsOptionType instances.
Yet another alternative is to implement a more fluid-style API in an extension. This can be helpful if you don't have a lot of consistent use cases and the number of enum cases isn't likely to grow. Something like this:
extension SubSettingsOptionType
{
func onStaticCell(_ code: (_ model: SubSettingsOption) throws) rethrows -> Self?
{
switch self
{
case staticCell(let model):
code(model)
return nil
default: return self
}
}
func onSwitchCell(_ code: (_ model: SubSettingsSwitchOption) throws) rethrows -> Self?
{
switch self
{
case switchCell(let model):
code(model)
return nil
default: return self
}
}
}
Then you can use it like this:
models[0].options[0]
.onStaticCell { $0.isSelected = true }
?.onSwitchCell { $0.handler() }
Although I usually like fluid API's, in this case, the need to optionally unwrap here makes it a bit more awkward. There are ways to get around that - wrap the enum, and keep track of which on... methods have been called. That would eliminate the need for the optional. I'll leave that "as an exercise" for now.
I have some code that is like the following
class Vibration: NSObject {
var status: VibrationStatus // an enum
}
and a function on another class (of type NSObject) like the following, that is part of an object that has a property vibration of type Vibration
func vibrate() {
DispatchQueue.main.async { [weak self] in
vibration.status = .vibrating
// do some real HW vibrate stuff
}
}
None of the properties or class definitions include #objc or #objcMembers
I am trying to create a test that will wait for that async call to set the vibration.status.
I have a test function that seems to work (see below) when I declare the status property as #objc or put #objcMembers on the Vibration class.
func testVibrate() {
let invite: SignalingInviteBody = SignalingInviteBody()
let incomingCall = IncomingCall(invite)
let expectation = XCTNSPredicateExpectation(predicate: NSPredicate(format: "status = 2"), object: incomingCall.vibration)
incomingCall.startRing() // this calls the function vibrate()
wait(for: [expectation], timeout: 3.0)
}
This test also requires #objc on the enum declaration with Objective-C compatible enum declaration, which I don't want as it would only be for testing.
Except for testing, there is no need to make the status property #objc or the Vibration class as #objcMembers and I would rather not change the base program code to a more inefficient style when I don't need the Objective-C compatibility in the base program.
Is there a way to unit test this in a real, honest to goodness, Swift way?
Preface: this is just some pseudo-code I nailed out quickly in the browser. It'll probably need some polishing before it compiles properly.
I would use a mock and dependency injection:
class MockVibration: Vibration {
let statusChanged: (VibrationStatus) -> Void
init(statusChanged: (VibrationStatus) -> Void) {
self.statusChanged = statusChanged
}
var status: VibrationStatus {
didSet {
statusChanged(status)
}
}
}
I would probably have a protocol, and have Vibration and MockVibration both conform to it, but having MockVibration: Vibration should work well. Once you've defined this mock, you can use it to fulfill an expectation in your test case:
func testVibrate() {
let didVibrate = self.expectation(description: "Started vibrating")
let mockVibration = MockVibration { newStatus in
XCTAssertEqual(newStatus, .vibrating)
didVibrate.fulfill()
}
let invite = SignalingInviteBody()
let incomingCall = IncomingCall(invite, mockVibration)
incomingCall.startRing() // this calls the function vibrate()
wait(for: [didVibrate], timeout: 3.0)
}
You might even be able to rework this interface so that DispatchQueue.main.async {} happens within the Vibration class as an internal detail. If you do that, the interaction between IncomingCall and Vibration becomes synchronous, so you wouldn't need to use expectations. Your incoming call test would reduce to:
class MockVibration {
// The mock can just have its status set simply/synchronously
var status: VibrationStatus = .off // or some other "initial" value
}
func testVibrate() {
let mockVibration = MockVibration()
let invite = SignalingInviteBody()
let incomingCall = IncomingCall(invite, mockVibration)
incomingCall.startRing() // this calls the function vibrate()
XCTAssertEqual(mockVibration.status, .vibrating)
}
Of course, then you'd need a separate test that covers Vibration, and ensures that its public APIs cause it to change its internal state using the right dispatch queue or whatever.
I have a service class, I would like to assert 2 things
A method is called
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*
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.
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)
}
}
I am working on testing an API through Alamofire. I need to make a single call to the server to prepare it for the integration test. Once that is done, I am ready to start running tests.
The usual override setUp() is run for every test, so I do not want to do that.
I have therefore chosen to override the class setUp() as described here: https://developer.apple.com/reference/xctest/xctestcase
That's all well and good, but now, I no longer can use the standard waitForExpectations. (In the class override setUp()) I get several compiler errors that tell me that I am no longer calling the same waitForExpectations because I am in a class method, not a test case.
To try to get around this, I wanted to use a semaphore like so:
class ServiceLayerTests: XCTestCase {
static var apiService: APIService = APIService()
let sessionManager = SessionManager(serverTrustPolicyManager: ServerTrustPolicyManager(policies: ["http://localhost:3000/": .disableEvaluation]))
static var companyManger: UserWebResource?
static var companyManagerID = -1
override class func setUp() {
apiService.baseURL = "http://localhost:3000/"
beginIntegrationTests()
}
class func beginIntegrationTests() {
var semaphore = DispatchSemaphore(value: 0)
apiService.beginIntegrationTests(completion: {resource, error in
if let resource = resource {
if let manager = resource as? UserWebResource {
companyManger = manager
companyManagerID = manager.id
semaphore.signal()
}
}
})
_ = semaphore.wait(timeout: DispatchTime.distantFuture)
}
}
This does not work. Under the hood, there is an alamo fire call to the server and it responds with the user to use for the integration tests. I do see the server spinning, so I know that the actual communication is happening, but I never get into the completion closure.
I suspect I am not understanding how Swift does semaphores and that I have created a deadlock somehow. If somebody has a better solution, I'd be more than happy to hear it.
I get several compiler errors that tell me that I am no longer calling
the same waitForExpectations because I am in a class method, not a
test case
That makes sense. What you probably want is to refactor so that you are in a test case:
override class func setUp() {
apiService.baseURL = "http://localhost:3000/"
}
func testIntegrationTests() {
let urlExpectation = expectation(description: "INTEGRATION TEST")
apiService.beginIntegrationTests(completion: {resource, error in
// ...
urlExpectation.fulfill()
})
// not sure what an acceptable timeout would be, I chose this at random
waitForExpectations(timeout: 25) { error in
if let error = error {
print("Error: \(error.localizedDescription)")
}
}
}
One of the best resources with some good test examples can be found here: http://nshipster.com/xctestcase/
You can create the expectation as a lazy var that executes your one-time set up and fulfills on completion.
At the beginning of your per-test setUp() function you can wait for that expectation.
None of your tests will run until it is fulfilled, and the initial setup will run only once.
class WaitForSetup_Tests: XCTestCase {
lazy var initialSetupFinished: XCTestExpectation = {
let initialSetupFinished = expectation(description: "initial setup finished")
initialSetupTask() { // your setup task that executes this closure on completion
initialSetupFinished.fulfill()
return
}
return initialSetupFinished
}()
override func setUp() {
wait(for: [initialSignOutFinished], timeout: 2.0)
// do your per-test setup here
}
}
Note: This solution avoids using the override class function setUp() class method, because I couldn't figure out how to use the expectations except for in an instance.