I'm looking for suggestions on good practices to test the equality of swift objects
If I have a complex object with many properties it's easy to miss the equality implementation.
What are the good practices to enforce the equality implementation?
More details:
Of course, you can write tests to check that two value are equal when they should, but the problem here is that this can break easily.
You have a struct in your project, after six months another developer adds a property to that struct.
Assume that he forgets to add the property to the equality. The equality still works, the tests pass but your app will break when only that property changes. My question is, there is a way to avoid this problem?
In a non trivial app is really easy to end up having model objects with 10 values, and use nested model objects. So how can I keep under control the equality of my model objects? How can I reduce the human error risk in the equality implementation?
Possible solution:
One solution I have in mind is to write a script that looks at compile time for classes and struct that conform to a certain protocol
Diffable : Equatable {}
For all the classes/structs that adopt Diffable I'll check for the existance of a test file, i.e.:
For the class Foo I'll look for a test class Foo_Tests_Diffable
than for all the properties inside Foo I'll check for the existance of a test function that conform to the following name pattern
test<property name>Diffable
i.e.:
class Foo : Diffable {
var title: String
var color: UIColor
var tag: Tag
}
I'll check for the following tests inside Foo_Tests_Diffable
func testTitleDiffable {
// Test
}
func testColorDiffable {
// Test
}
func testTagDiffable {
// Test
}
If all the script finds all the expected tests than the compile step pass, otherwise it fails
But this solution it's time consuming and I don't know if I'll be able to implement it, so any suggestion is welcome
I would create unit tests specifically for this purpose.
class YourObjectEquatableTests: XCTestCase
{
func testPropertyOne()
{
var object1 = /* ... */
var object2 = /* ... */
object1.propertyOne = nil
object2.propertyOne = nil
XCTAssertEqual(object1, object2)
object1.propertyOne = "different value"
XCTAssertNotEqual(object1, object2)
/* etcetera */
}
func testPropertyTwo()
{
/* ... */
}
}
Note: if you write your test cases before implementing the Equatable details, you're actually doing TDD which is very cool!
I didn't try it a lot but Swift has Reflection capabilities.
https://appventure.me/2015/10/24/swift-reflection-api-what-you-can-do/
So in your case, you can mirror a class and then put some conditions in your test.
for example, you can set a fix number or attribute or methods for a class in your test.
let nbAttributes = 6
and then check it:
aMirror.children.count
I wrote a simple example in the playground and if I add a method variable, the children count increases. I don't know if it's possible to check the methods though.
class toto{
var io = 9
var tutu = "yo"
func test(){
}
}
let toto1 = toto()
let aMirror = Mirror(reflecting: toto1)
aMirror.children.count
another link:
Reflection in swift 2
Hope that helps :)
Yes, expanding on #Mikael's answer, you should probably use Reflection.
An example would be:
String(reflecting: obj1) == String(reflecting: obj2)
Related
Long time IOS developer/tinkerer here. I mostly taught myself programming, OBJ-C back in the day and now Swift. So apologies in advance if things I ask are too basic, its partly because I may not be well versed on some fundamentals.
I am currently working on an app. Alongside it I have been reading a fair bit on writing testable code and testing in general. I am not talking about purely TDD but I would like the libraries that I am creating for the app to have a good testset available. Partly because its good practice and partly because its soemthing I want to learn to do better.
So here goes, in my app class(es) I have a number of functions that take in parameters and give an output (as you do!). However, a number of these functions also make changes to class properties as data in these properties will be used in other class functions. For example:
class SomeClass() {
var someArrayProperty: [String] = []
var someInputParameter: String
init(input: String) {
//some initialisation code
self.someInputParameter = input
//Call function to build object
let object = self.buildObject(inputParameter: self.someInputParameter)
}
func buildObject(inputParameter: String) -> SomeObject {
let objectToReturn = SomeObject(withInputParameter: inputParameter)
let stringToAddToArray = "Object 1 created"
self.someArrayProperty.append(stringToAddToArray)
return objectToReturn
}
}
From what I have read about the testing, the code should ideally be such that it should do one job and not change something outside of the code as it becomes untestable for complex code. Here, the issue I have is that I am directly changing the someArrayProperty from within the method, i.e. changing something outside of the method.
Although its not an issue for the code and everything works fine, I would like to understand what do you guys feel about things like this from a testing point of view in your own code? And what pattern/changes you generally follow in your own code to avoid it?
Again, apologies if its too basic a question but I think it will help me fill in gaps in my knowledge to be able to write more beautiful code rather than something that just works and breaks next time a minor update is done somwhere. :)
Thanks
So if your function is called buildObject, it should do job inside it and have no return value. But if you call it buildedObject, it should return constructed object. You can read more about in Apple Naming Methods documentation.
And so your code should look like this:
class SomeClass() {
var someArrayProperty: [String] = []
var someInputParameter: String
init(input: String) {
//some initialisation code
self.someInputParameter = input
//Call function to build object
let object = self.buildedObject(inputParameter: self.someInputParameter)
// Other code which modifies data
let stringToAddToArray = "Object 1 created"
self.someArrayProperty.append(stringToAddToArray)
}
func buildedObject(inputParameter: String) -> SomeObject {
let objectToReturn = SomeObject(withInputParameter: inputParameter)
return objectToReturn
}
}
Say you
protocol Able: class {
var v:UIView? { get set }
var x:CGFloat { get set }
}
then of course, when you use Able,
if you forget "v" or "x"...
it is an error. That's good.
So do this:
class ScreenThing: UIViewController, Able {
#IBOutlet var v: UIView?
var x: CGFloat = 0.0
}
All's well. That's great.
It is enforced that you specify "v" and "x" and indeed initialize them.
But. Try this...
var _H: UInt8 = 0
protocol Able: class {
}
extension Able where Self:UIViewController {
var p:P {
get {
return objc_getAssociatedObject(self, &_H) as! P
}
set {
objc_setAssociatedObject(self, &_H, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
__setter()
}
}
}
Able now has a property p.
You can use p perfectly either in functions in Able, or, in functions in ScreenThing. That's great.
However.....
When you do this.....
class ScreenThing: UIViewController, Able {
}
you do not get an error.
You can forget to initialize "p" (it will crash).
Indeed you don't have to specify "p" as a variable (as you must with "v" and "x").
Why is it so?
This seems like a huge problem.
Is there something different I have to do, to, make the compiler enforce "p", just as it of course normally enforces variables in protocols?
Another way to look at this question:
Given exactly my code above:
is there a way to enforce the compiler to need an initializer for "p" in the consumer class?
For example.
I tried this...
class ScreenThing: UIViewController, Able {
var p:P
}
But that doesn't work.
(Strangely, that compiles - actually I don't know what the hell it's doing! It seems to be a "different" p from the p in the Extension. But in any event it doesn't enforce the need for an initializer.)
In short, again, is there something I could do or add, above, that would make the compiler enforce me initializing the pseudo-property-thing, just as of course it normally does when I put a property in the protocol such as "x" or "v".
?
maybe I have to add some ordinary property in the protocol (like "pp"), and somehow make p related to that in some way?? Just a thought.
(Footnote -- see this to understand the ": class" needed in the protocol above.)
Answering my own question:
My confusion above is that there is nothing to initialize. The var p:P in the extension (with the get and set code blocks) is simply two functions.
There's nothing to initialize.
So for example: in my extra question, I ask "how to force conforming classes initailize it on wake up?" That is meaningless. If anything, one could ask: "how to force conforming classes be sure to 'use those functions' on wake up?" - which has nothing to do with initialization.
Note too that my specific example code in the computed variable, happens to (unrelatedly) use a variable that doesn't get initialized - leading to confusion.
You don't have to implement p in the protocol's adopter, because the protocol extension has supplied an implementation. That is what a protocol extension is.
Simpler example:
protocol P {}
extension P {
func greet() {print("hello")}
}
class C : P {}
C().greet()
Note that (1) that compiles even though C does not declare greet and (2) it runs even though C does not contain an implementation of greet. That's because that's the job of the protocol extension.
Consider the following class named SomeClass written in Swift:
#objc class SomeClass: NSObject
{
var shouldCallBar = false
func foo()
{
if (shouldCallBar == true)
{
bar()
}
}
func bar()
{
}
}
For testing the above class foo() method (and similar scenarios mostly written in Objective-C) I was using OCMock like:
- (void) testFooBarShouldBeCalledWhenShouldCallBarIsTrue
{
SomeClass * someClass = [SomeClass new];
// Create mocks.
id mockSomeClass = OCMPartialMock(someClass);
// Expect.
[[mockSomeClass expect] bar];
// Stub.
someClass.shouldCallBar = YES;
// Run code under test.
[someClass foo];
// Verify.
[mockSomeClass verify];
// Stop mocking.
[mockSomeClass stopMocking];
}
But above test fails with Swift code as OCMock won't works well with Swift.
So I am considering something like entirely in Swift:
class SomeClassTests: XCTestCase
{
class MockSomeClass: SomeClass
{
var isBarCalled = false
override func bar()
{
isBarCalled = true
}
}
func testBarShouldBeCalledWhenTrue()
{
let someClass = MockSomeClass()
someClass.shouldCallBar = true
someClass.foo()
XCTAssertTrue(someClass.isBarCalled == true)
}
}
Note here I am subclassing the original class under test and overriding the bar(). I am not at all touching the foo() implementation.
But the downside is I am using MockSomeClass instance to test foo() of SomeClass. This is something I don't like and not recommended.
Is there any better solution to the problem above?
Notes:
I am not talking about Dependency Injection here. Dependency Injection is entirely different approach.
I face these kind of issues when testing UI code in UIViewController.
I have thought of Protocol based programming but was not able to come up with solution to problem above.
So, you want to test that one method (foo) does or does not call another method (bar). The foo method is the one under test, and the bar method is, in the wider sense, a dependent component.
If the invocation of bar has lasting side effects, you could get away with testing that the side effect is/isn't present, maybe by querying a property or similar. In that case you don't need mocks or similar.
If there are no side effects then you must substitute the dependency. To do so you need a seam at which you place code that can tell the test whether the method has been invoked or not. For that, I can only see the two options that Jon already discussed in the question you refer to.
You either put the two methods into separate classes, in which case the class boundary is the seam. Using a protocol, or just an informal convention, you can then completely replace the class that implements bar. Dependency injection comes in handy here.
If the two methods must stay in the same class then you have to use a subclass boundary as a seam, i.e. you use the fact that you can override methods and implement a test-specific sublass. It's easiest when you can use a mock framework. If that's not an option you have to write the code yourself, much like what you describe in your question.
As we transition our brains from Object Oriented Programming to Protocol Oriented Programming how can I do the following ?
let's say I have a JSON object representing Model has {created_time,updated_time,type,...} and those values are common in 5 Model objects.
is it right to make a protocol contains all the above properties like the following
protocol xxx {
var type : String { get }
var updatedTime : String { get }
var createdTime : String { get }
//...//
}
and then all the 5 structs conform to this protocol
I would say that's a perfectly good solution. The alternative would be having a base class with those properties and have all five of those models inherit from the base class, but there's no particular reason to use inheritance here.
A protocol is just a "contract" that guarantees a class has certain properties or behavior. To me, your example here feels very "contractual."
By contrast, inheritance implies a "is-a" relationship (e.g. a Ford is-a car). To me, this feels more like a contract than an "is-a" case. Of course, neither choice is wrong, but think your protocol idea here is good.
Speaking of Protocol Oriented Programming Swift 2 has protocol extensions which allow default implementations. This also replaces many cases where you would use a superclass instead.
In this case:
// extension of your example protocol
extension xxx {
var type : String { return "a type" }
var updatedTime : String { return "00:00" }
var createdTime : String { return "00:00" }
//...//
}
// struct which conforms to it
struct Astruct: xxx {}
// using the properties
Astruct().type
Astruct().updatedTime
if all properties and methods have a default implementation by the protocol extension you don't have to provide any yourself. However you can "override" them only by implementing them.
So you're decision is right and you should use protocols as often as possible.
The only big drawback is that there is no super where you can explicitly call the default implementations. A workaround (see this answer) would require a superclass which makes the protocol almost redundant.
I'm having trouble grasping the proper way of instantiating variables that always need to be set before an object is fully functional but may need to be instantiated after the constructor. Based on Swift's other conventions and restrictions it seems like there is a design pattern I'm unaware of.
Here is my use case:
I have a class that inherits from UIViewController and will programmatically create views based on user actions
I need to attach these views to this class, but to do so I need to retrieve their content based on configuration data supplied by another controller
I don't care if this configuration data is passed to the constructor (in which case it would always be required) or supplied by a secondary call to this object before it is used
My problem seems to be that both of the approaches in bullet 3 seem flawed.
In the first case, there is only one legitimate constructor this class can be called with, yet I'm forced to override other constructors and initialize member variables with fake values even if the other constructors are never intended to be used (I'm also trying to keep these variables as let types based on Swift's best practices).
In the second case, I'm effectively splitting my constructor into two parts and introduce an additional point of failure in case the second part fails to be called prior to class being used. I also can't move this second part to a method that's guaranteed to be called prior to usage (such as viewDidLoad) because I still need to pass in additional arguments from the config. While I can make sure to call the initPartTwo manually, I'd prefer to have a mechanism that better groups it with the actual constructor. I can't be the first one to run into this and it seems like there is a pattern I'm not seeing to make this cleaner.
UPDATE:
I ended up going with a modified version of the pattern matt suggested:
struct Thing {
let item1: String
let item2: String
struct Config {
let item3: String
let item4: String
}
var config:Config! {
willSet {
if self.config != nil {
fatalError("tried to initialize config twice")
}
}
}
init() {
self.item1 = ...
self.item2 = ...
...
}
public func phaseTwoInit(item3: String, item4: String) {
self.item3 = item3
self.item4 = item4
...
}
}
var t = Thing()
...
t.phaseTwoInit(...)
...
// start using t
If an initial instance variable property value can't be supplied at object initialization time, the usual thing is to declare it as an Optional. That way it doesn't need to be initialized by the class's initializers (it has a value - it is nil automatically), plus your code subsequently can distinguished uninitialized (nil) from initialized (not nil).
If the Optional if an implicitly unwrapped Optional, this arrangement need have no particular effect on your code (i.e. it won't have to be peppered with unwrappings).
If your objection is that you are forced to open the door to multiple settings of this instance variable because now it must be declared with var, then close the door with a setter observer:
struct Thing {
var name:String! {
willSet {
if self.name != nil {
fatalError("tried to set name twice")
}
}
}
}
var t = Thing()
t.name = "Matt" // no problem
t.name = "Rumplestiltskin" // crash