XCTest class func setUp() method how to handle failures - swift

So Apple has an example of how the setUp and TearDown methods should be used that looks like this:
class SetUpAndTearDownExampleTestCase: XCTestCase {
override class func setUp() { // 1.
// This is the setUp() class method.
// It is called before the first test method begins.
// Set up any overall initial state here.
}
override func setUpWithError() throws { // 2.
// This is the setUpWithError() instance method.
// It is called before each test method begins.
// Set up any per-test state here.
}
override func setUp() { // 3.
// This is the setUp() instance method.
// It is called before each test method begins.
// Use setUpWithError() to set up any per-test state,
// unless you have legacy tests using setUp().
}
func testMethod1() throws { // 4.
// This is the first test method.
// Your testing code goes here.
addTeardownBlock { // 5.
// Called when testMethod1() ends.
}
}
func testMethod2() throws { // 6.
// This is the second test method.
// Your testing code goes here.
addTeardownBlock { // 7.
// Called when testMethod2() ends.
}
addTeardownBlock { // 8.
// Called when testMethod2() ends.
}
}
override func tearDown() { // 9.
// This is the tearDown() instance method.
// It is called after each test method completes.
// Use tearDownWithError() for any per-test cleanup,
// unless you have legacy tests using tearDown().
}
override func tearDownWithError() throws { // 10.
// This is the tearDownWithError() instance method.
// It is called after each test method completes.
// Perform any per-test cleanup here.
}
override class func tearDown() { // 11.
// This is the tearDown() class method.
// It is called after all test methods complete.
// Perform any overall cleanup here.
}
}
As you can see in the first method override class func setUp() overall inital state is suppost to be set. I have a simple setup phase for the application. But if this fails in some way. Then the test just get ignored like it never ran instead of marked as failure.
This is what the log says:
:0: error: simpleUITest : Failed to get matching snapshot: No
matches found for first query match sequence: Descendants matching type Cell -> Elements matching predicate '"login_button" IN identifiers', given input App element pid: 9849 (no attribute values
faulted in) Test Suite 'simpleUITest' failed at 2021-01-20
10:49:42.684. Executed 0 tests, with 1 failure (0 unexpected) in
0.000 (13.204) seconds
How can I get this to report as a test failure rather then just having XCTest ignore it?

Based on the docs, you have at least two options to fail a test from setUp:
Before each test begins, XCTest calls setUpWithError(), followed by setUp(). If state preparation might throw errors, override setUpWithError(). XCTest marks the test failed when it catches errors, or skipped when it catches XCTSkip.
and
Tip
You can include test assertions in a test case's setUp(), setUpWithError(), tearDown(), and tearDownWithError() instance methods. Any such assertions will be evaluated as part of every test method's run.
It isn't clear what you tried from your question. Do you have an assertion in setUp as suggested in the second option? Note that this only applies to the non-class setUp

Related

An error occurs in the overridden function of XCUITest

An error occurs in the overridden function of XCUITest.
I have never mentioned XCUITest.
The error is: "Method does not override method in superclass".
This happens when you change Xcode from version 11.4 beta 2 to 11.3.1 and build.
what should I do?
import XCTest
class SampleAppUITests: XCTestCase {
override func setUpWithError() throws {
// Put setup code here. This method is called before the invocation of each test method in the class.
// In UI tests it is usually best to stop immediately when a failure occurs.
continueAfterFailure = false
// In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
}
override func tearDownWithError() throws {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}
func testExample() throws {
// UI tests must launch the application that they test.
let app = XCUIApplication()
app.launch()
// Use recording to get started writing UI tests.
// Use XCTAssert and related functions to verify your tests produce the correct results.
}
func testLaunchPerformance() throws {
if #available(macOS 10.15, iOS 13.0, tvOS 13.0, *) {
// This measures how long it takes to launch your application.
measure(metrics: [XCTOSSignpostMetric.applicationLaunch]) {
XCUIApplication().launch()
}
}
}
}
Explained in the release notes:
https://developer.apple.com/documentation/xcode_release_notes/xcode_11_4_beta_3_release_notes
You (the template) are calling methods that don’t exist when you use Xcode 11.3.1 or before. Use availability to replace setUpWithError and tearDownWithError (Swift 5.2) with simple setUp and tearDown (Swift 5.1).

Debugging window doesn't show print statements

I've been able to see print statements in the debugging window for my app. When I create a 'mock up' program (small trial app) to learn about Swift testing, none of the print statements in my LifecycleTests.swift file under the FirstTests folder display in the debugging window.
import XCTest
class LifecycleTests: XCTestCase {
override class func setUp() {
// Put setup code here. This method is called before the invocation of each test method in the class.
print("In class setUp.")
// NSLog("In class setUp.")
}
override class func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
print("In class tearDown.")
// NSLog("In class tearDown")
}
override func setup() {
print("In setup.")
// NSLog("In setup")
}
override func tearDown() {
print("In tearDown.")
// NSLog("In tearDown.")
}
func testExample() {
print("Starting test.")
// NSLog("Starting test.")
addTearDownBlock {
print("In first tearDown block.")
// NSLog("In first tearDown block.")
}
// print("In middle of test.")
NSLog("In middle of test.")
addTearDownBlock {
print("In second tearDown block.")
// NSLog("In second teardown block.")
}
print("Finishing test.")
// NSLog("Finishing test.")
}
func testPerformanceExample() {
// This is an example of a performance test case.
self.measure {
// Put the code you want to measure the time of here.
}
}
}
You can only get console output from one target at a time. Your console output is set to come by default from your app target, not your test target.
If you just run a test containing a print statement, you won't see any debugger output:
That test has a print statement, but we ran it and nothing appeared in the console.
Okay, but now let's trick the console into seeing the input from the test target. To do so, put a breakpoint in the test:
We step over the print statement and it prints to the console, along with a lot of other useful info about the test:
The interesting thing is that once you've used this trick, you can take the breakpoint away. The test target continues to be the console source until you run the app itself again.
The trick is a weird one but it appears to be the only way. Apple actually acknowledges it in an allusive way in their docs where they say:
if you have been actively engaged in debugging, any output from the debugging session also appears [in the console]
Evidently "actively engaged in debugging" means you've put a breakpoint in the test.

XCTestCase optional instance variable

Why is my optional instance variable nil when I infact set it to non-nil?
Code:
class FooTests: XCTestCase {
var foo: Int?
func test_A_setFoo() {
XCTAssertNil(foo)
foo = 1
XCTAssertNotNil(foo)
}
func test_B_fooIsNotNil() {
XCTAssertNotNil(foo)
}
}
test_A_setFoo()succeeds while test_B_fooIsNotNil() fails
From Flow of Test Execution
(emphasis added):
For each class, testing starts by running the class setup method. For each test method, a new instance of the class is allocated and its instance setup method executed. After that it runs the test method, and after that the instance teardown method. This sequence repeats for all the test methods in the class. After the last test method teardown in the class has been run, Xcode executes the class teardown method and moves on to the next class. This sequence repeats until all the test methods in all test classes have been run.
In your case, test_B_fooIsNotNil() is executed on a fresh instance,
for which the foo property is nil.
Common setup code can be put into the setUp() class method
or setUp() instance method, see
Understanding Setup and Teardown for Test Methods

XCTest Single Asynchronous SetUp With Semaphores

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.

Unable to access Main Target Methods when Adding Tests to OSX Project in Swift

I have tried adding a testing bundle to my Project which seems to have succeeded.
However when I try to create instances of the Classes in my main project- I am unable to see them.
The Project seems to build fine - but I can' instantiate any of the test objects
Any ideas how to access them
Example Class to Test:
class EmailHelper: NSObject {
func generateEmailBody (greeting: String, bodyContent: String) -> String {
//Content goes in here
return message
}
}
import XCTest
class MyProject_DesktopTests: XCTestCase {
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
func testExample() {
// The Test would go in here but I can't seem to resolve EmailHelper class- it generates a lint error
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
}
func testPerformanceExample() {
// This is an example of a performance test case.
self.measureBlock {
// Put the code you want to measure the time of here.
}
}
}
I managed to get it working by adding Testable to the top of the class( This appears to be OSX specific issue)
import XCTest
#testable import MyProjectName // <--- This was the missing bit.... :)
class MyProject_DesktopTests: XCTestCase {
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
func testExample() {
// The Test would go in here but I can't seem to resolve EmailHelper class- it generates a lint error
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
}
func testPerformanceExample() {
// This is an example of a performance test case.
self.measureBlock {
// Put the code you want to measure the time of here.
}
}
}
Also be sure to clean your project after adding it an it seems to work.