Swift - Unit Testing Private Variables and Methods

How to unit test that private method is called in Swift

Tests can't access anything declared private. They can access anything declared internal as long as the test code does @testable import.

When you get that queasy feeling, "But I shouldn't have to expose this," consider that your class actually has multiple interfaces. There's the "everything it does interface" and there's the "parts needed by production code interface." There are various things to consider about this:

  • Is there another type that is trying to get out?
  • Is there another protocol to express a subset of the interface? This could be used by the rest of production code.
  • Or maybe it's like a home theater amplifier where "controls you don't need that often" are hidden behind a panel. Don't sweat it.

how to test methods which set private variables in viewcontroller unit testing?

You can still unit test those values below by adding public getters


protocol ViewControllerProtocol {
func setbackgroundImage(_ image : UIImage)
func setLogoImage(_ image : UIImage)
func setLoginButtontitle(_ title: String)
func setSignupButtonTitle(_ title: String)
func setTitleLabel(_ title: String)
func setContinuewithoutsignupTitle(_ title: String)
func getbackgroundImage() -> UIImage?
func getlogoImage() -> UIImage?
func getloginButtonTitle() -> String?
func getsignupButtonTitle() -> String?
}

class MainTest: XCTestCase {

let viewController = ViewController()

override func setUp() {
super.setUp()
}

func testExample() {
guard let testImage1 = UIImage(named: "testImage1") else {
XCTFail("Could not load test image 1")
return
}
viewController.setbackgroundImage(testImage1)
XCTAssert(viewController.getbackgroundImage() == testImage1)
guard let testImage2 = UIImage(named: "testImage2") else {
XCTFail("Could not load test image 2")
return
}
viewController.setLogoImage(testImage2)
XCTAssert(viewController.getlogoImage() == testImage2)
viewController.setLoginButtontitle("test")
XCTAssert(viewController.getloginButtonTitle() == "test")
viewController.setSignupButtonTitle("test")
XCTAssert(viewController.getsignupButtonTitle() == "test")
}

}

Testing a class which preserves its state in private variables

After researching and discussing with some experts, I come up with the solution that if we want to test a class which preserve it's state then the functionality which is preserving the state should go under a separate class. Which will serve the same purpose as setting the variables as private. So, ZoneUpdateDetector should have a dependency for example: ZoneUpdateStatePreserver and it should keep the state which was previously inside ZoneUpdateDetector

Swift Unit Test how to set with a private setter

What they were referring to is to place an extension in the file with the class-under-test. So if you have something like:

class ToBeTested {
private(set) var bool: Bool
}

You can add an internal setter:

extension ToBeTested {
func setBoolTrue() { bool = true }
}

But this has to be in the same file with ToBeTested. If you cannot modify that file, this is not a solvable problem. If it needs to be tested this way, then the class will need to be designed to permit that.

When a property is marked private(set), the compiler can assume that the value cannot be changed in any way outside this file. That allows it to apply optimizations that may not be valid if the property were changeable outside this file. The "setter method" may not even exist if the compiler determines that it doesn't need it. For example, it may completely inline that operation, or eliminate it entirely if it can prove the value is never changed.

Unit test and private vars

(Note that Swift 2 adds the @testable attribute which can make internal methods and properties available for testing. See @JeremyP's comments below for some more information.)

No. In Swift, private is private. The compiler can use this fact to optimize, so depending on how you use that property, it is legal for the compiler to have removed it, inlined it, or done any other thing that would give the correct behavior based on the code actually in that file. (Whether the optimizer is actually that smart today or not, it's allowed to be.)

Now of course if you declare your class to be @objc, then you can break those optimizations, and you can go poking around with ObjC to read it. And there are bizarre workarounds that can let you use Swift to call arbitrary @objc exposed methods (like a zero-timeout NSTimer). But don't do that.

This is a classic testing problem, and the classic testing answer is don't test this way. Don't test internal state. If it is literally impossible to tell from the outside that something has happened, then there is nothing to test. Redesign the object so that it is testable across its public interface. And usually that means composition and mocks.

Probably the most common version of this problem is caching. It's very hard to test that something is actually cached, since the only difference may be that it is retrieved faster. But it's still testable. Move the caching functionality into another object, and let your object-under-test accept a custom caching object. Then you can pass a mock that records whether the right cache calls were made (or networking calls, or database calls, or whatever the internal state holds).

Basically the answer is: redesign so that it's easier to test.

OK, but you really, really, really need it... how to do it? OK, it is possible without breaking the world.

Create a function inside the file to be tested that exposes the thing you want. Not a method. Just a free function. Then you can put that helper function in an #if TEST, and set TEST in your testing configuration. Ideally I'd make the function actually test the thing you care about rather than exposing the variable (and in that case, maybe you can let the function be internal or even public). But either way.

Objective C - how to test private variables

You can use KVC [obj setValue:<value> forKey:<key>] detail about KVC click here



Related Topics



Leave a reply



Submit