Search code examples
swiftunit-testingstructxctestxctestcase

Using setUp to run object creation once before all tests


I've a struct that returns lists of various kinds depending on received arguments. Those lists are lengthy and take time to create. I would like to write unit tests checking whether generated lists are of required characterictis (length, content, etc.)

Problem

I would like to instantiate different versions of that struct once per all tests using:

override class function setUp

When I try I get the error:

instance member 'shortWords' cannot be used on type 'TestSampleWords'

Example

SampleWords.swift

import Foundation

struct SampleWords {
    var wordSize: String

    private var length: Int {
        switch wordSize {
        case "short":
            5
        case "long":
            10
        default:
            7
        }
    }

    private func randomString(length: Int) -> String {
        // Source: https://stackoverflow.com/a/26845710/1655567
        let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
        return String((0..<length).map{ _ in letters.randomElement()! })
    }
    
    var wordsSet: [String] {
        (0..<1001).map { _ in randomString(length: length) }
    }
}

TestSampleWords.swift

import XCTest
@testable import SampleWords

final class TestsSampleWords: XCTestCase {

    private var longWords: SampleWords!
    private var shortWords: SampleWords!
    
    // I'm adding 'class' modifier as I want for the setUp to run only once
    override class func setUp()  {
        // At this junction I want to create a set of objects 
        // reflecting struct parameters and re-use them across tests
        longWords = SampleWords(wordSize: "long")
        shortWords = SampleWords(wordSize: "short")
    }

    override class func tearDown() throws {
        longWords = nil
        shortWords = nil
    }

    func testAreLists() {
        XCTAssertTrue(longWords.wordsSet is [String])
        XCTAssertTrue(shortWords.wordsSet is [String])
        // Further tests continue in this and other methods
    }


}

Notes

  • The test will work if override class function setUp is replaced with override function setUp; however, this is not desired behaviour. I want to create a set of objects and re-use those in different tests.

Solution

  • You cannot reference instance variables (private var longWords, private var shortWords) from a static/class context inside the class func.

    The solution is as simple as they come - just make those variables also static/class :

    private class var longWords: SampleWords!
    private class var shortWords: SampleWords!
    

    Then you will be able to assign to them in the class func setUp. A small downside will be that when used in test functions, which are not static, you will have to refer to them either as TestsSampleWords.longWords or Self.longWords (note the capital S in Self here).