---
title: Testing
slug: v4-5/swift/quick-tips/testing
docTags: 
createdAt: 2023-10-02T17:37:01.939Z
---

Testing peer-to-peer systems can be challenging, but worth the initial effort to ensure your data model works across multiple instances. It is important to test your business logic with end-to-end unit tests that involve multiple Ditto peers.

A typical test lifecycle is:

1. Create two instances of Ditto with the same identity and different directories.
2. Call `startSync()` on both instances.
3. Listen for changes to data using `subscribe()` and write tests as a response to the `observeLocal()` function.
4. Clean up Ditto directories between tests and stop any open synchronization threads using `stopSync()`.

For complete examples and running tests, see the [open source samples GitHub repository](https://github.com/getditto/samples/tree/master/testing/).

# Getting Started[​](https://legacydocs.ditto.live/ios/quick-tips/testing#getting-started)

To test Ditto using SwiftUI, we provide you with some basic convenience functions you can use to get started.

For code that you can use in your project, [see the example on GitHub](https://github.com/getditto/samples/tree/master/testing/SwiftUI).

# Create custom directories[​](https://legacydocs.ditto.live/ios/quick-tips/testing#create-custom-directories)

Each instance of Ditto should use a different directory. You can create a convenience class to provide a custom directory for every instance of Ditto.

```swift
/// Test helper which wraps a Ditto instance.
public class DittoInstance {
    internal var ditto: Ditto?
    private let instanceDir: URL
    
    public init(appID: String) {
        // No need for cleanup, as the TestsSetup class is configured as NSPrincipalClass
        // and will delete topLevelDittoDir() before any test job is run

        let instanceDir = topLevelDittoDir()
            .appendingPathComponent(appID)
            .appendingPathComponent(UUID().uuidString)

        self.instanceDir = instanceDir
        self.ditto = Ditto(
            identity: .onlinePlayground(appID: appID, persistenceDirectory: instanceDir),
            persistenceDirectory: instanceDir
        )
    }
    
    public func stop() {
        self.ditto!.stopSync()
        self.ditto = nil
    }
}

func topLevelDittoDir() -> URL {
    let fileManager = FileManager.default
    return try! fileManager.url(
        for: .documentDirectory,
        in: .userDomainMask,
        appropriateFor: nil,
        create: false
    ).appendingPathComponent("ditto_top_level")
}
```

# Create your test[​](https://legacydocs.ditto.live/ios/quick-tips/testing#create-your-test)

1. Initialize two ditto instances with different directories.
2. Call `startSync()` on both instances.

```swift
import XCTest
import DittoSwift


final class SwiftUITests: XCTestCase {
   
    var ditto1: DittoInstance!
    var ditto2: DittoInstance!
    
    override func setUp() {
        self.ditto1 = DittoInstance(appID: "test-app")
        self.ditto2 = DittoInstance(appID: "test-app")
        try! self.ditto1.ditto?.startSync()
        try! self.ditto2.ditto?.startSync()
    }
    
    func testExample() throws {
      // your test here
    }
}
```

# Listen for Data Changes[​](https://legacydocs.ditto.live/ios/quick-tips/testing#listen-for-changes-to-data)

Using `observeLocal()`, we listen to changes to data. We write tests as a response to the `observeLocal()` function based on what we expect to happen.

The tests should reside within the callback so that they properly test the state of the database after synchronization is complete.

```swift
...
let initialResultExpectation = expectation(description: "Initial event received")
let docID = try! ditto1.ditto!.store.collection("cars").upsert(["make": "toyota", "color": "red"])
let subscription = ditto2.ditto!.store.collection("cars").findAll().subscribe()
let liveQuery = ditto2.ditto!.store.collection("cars").findByID(docID).observeLocal { doc, event in
    if (!event.isInitial) {
        XCTAssertEqual(doc!.value["make"] as! String, "toyota")
        initialResultExpectation.fulfill()
    }
}

wait(for: [initialResultExpectation], timeout: 2)
```

# Clean up[​](https://legacydocs.ditto.live/ios/quick-tips/testing#clean-up)

Don't forget to clean up between tests. This can also be implemented as part of a base test class.

```swift

func testExample() throws {
  let initialResultExpectation = expectation(description: "Initial event received")
  let docID = try! ditto1.ditto!.store.collection("cars").upsert(["make": "toyota", "color": "red"])
  let subscription = ditto2.ditto!.store.collection("cars").findAll().subscribe()
  let liveQuery = ditto2.ditto!.store.collection("cars").findByID(docID).observeLocal { doc, event in
      if (!event.isInitial) {
          XCTAssertEqual(doc!.value["make"] as! String, "toyota")
          initialResultExpectation.fulfill()
      }
  }
  
  wait(for: [initialResultExpectation], timeout: 2)
  liveQuery.stop()
  ditto1.stop()
  ditto2.stop()
}
```

# Full example[​](https://legacydocs.ditto.live/ios/quick-tips/testing#full-example)

For the full example that you can use in your project, [see the open source testing library on GitHub](https://github.com/getditto/samples/tree/master/testing/android/app/src/androidTest/java/live/ditto/dittotesting).\`
