0

I am using nimble in unit test expectation mapping and having a question about comparing structs.

What i am observing is that the matched .to(be(x)) does not work at all with structs. So this following unit test fails:

func someTest() {
    struct Struct {
        let a: String
        let b: String
    }
    let structure = Struct(a: "a", b: "b")
    expect(structure).to(be(structure))
}

Does this mean that the copy on write mechanism kicks in here and we are looking on 2 copies? Why is that test failing?

Dávid Pásztor
  • 51,403
  • 9
  • 85
  • 116
Martin Mlostek
  • 2,755
  • 1
  • 28
  • 57

1 Answers1

2

The be() function actually calls beIdenticalTo, which uses pointer equality checking, so it only works for reference types. See the BeIdenticalTo source code.

You should make Struct conform to Equatable and use equal instead.

func someTest() {
    struct Struct: Equatable {
        let a: String
        let b: String
    }
    let structure = Struct(a: "a", b: "b")
    expect(structure).to(equal(structure))
}
Dávid Pásztor
  • 51,403
  • 9
  • 85
  • 116
  • Ah, is it possible with nimble to actually compare the address where the pointer is pointing to? – Martin Mlostek Jun 26 '20 at 11:11
  • @MartinMlostek that's not the Swift way of testing and especially not value types. You could use `RawPointer`s, but really - you shouldn't. With value types you should never care about object equality on the memory level - you should only care about the properties being equal. – Dávid Pásztor Jun 26 '20 at 11:13
  • I agree, i was just curious in that small example to play around (and actually learn) the copy-on-write behavior – Martin Mlostek Jun 26 '20 at 11:16
  • 1
    @MartinMlostek there's no copy-on-write behaviour in your example. Copy-on-write is an extra optimisation only implemented by some built-in types (such as `Array` and `Dictionary`). For custom types, you'd need to implement it yourself. By default, value types are copied on each assignment, not only when mutating them. You don't mutate `structure`, so if there was copy-on-write, your test would pass. – Dávid Pásztor Jun 26 '20 at 11:20
  • But copy on write is also implemented in `String` right? This means assigning `let structureB = structure` would not lead to a duplication of the struct, am i correct? (sorry for going offtopic) – Martin Mlostek Jun 26 '20 at 11:24
  • I am not familiar with Nimble, but I wonder if that is a side-effect of the (in)famous “anything can be cast to an object” – both structs are wrapped in (seperate) `SwiftValue` objects. – Martin R Jun 26 '20 at 11:25
  • @MartinMlostek yes, but there's no write operation on any of the `String`. And you are testing the parent `struct` itself, which doesn't implement copy-on-write, so it doesn't matter if its properties by themselves do implement it. – Dávid Pásztor Jun 26 '20 at 11:52
  • 1
    Copy-on-write really has absolutely nothing to do with structs. It just so happens that all the CoW types in the standard library happen to be structs. It's a manually implemented pattern, that could just as easily be implemented on a class. In the case of assignment of `String`, there's absolutely nothing unusual happening. Strings are structs, and the struct is copied into the new variable. But the string struct is only 3 machine words (IIRC). All of the actual character data is stored in a heap-allocated buffer. That buffer is what's copied on mutation (if there's no other owning references – Alexander Jun 26 '20 at 15:12