App Academy

Software Stuff

Preventing Retain Cycles in Automated Tests Using addTearDown

When writing automated tests, it is important to prevent retain cycles. A retain cycle occurs when two objects have a strong reference to each other. This can happen when an object creates a closure that captures another object. If the closure is not released, the two objects will never be deallocated, which can lead to memory leaks.

In production code, there are a few ways to prevent retain cycles:

Weak References

One way is to use weak references. A weak reference is a reference that does not prevent the object from being deallocated. To use a weak reference, you can use the weak keyword when declaring the variable.

weak var viewController: ViewController?

Unowned references

Another way to prevent retain cycles is to use the unowned keyword. An unowned reference is similar to a weak reference, the difference is, it implicitly wraps the weak reference. An unowned reference cannot be nil, so if the object that the unowned reference refers to is deallocated, the app will crash.

unowned var collectionView: UICollectionView?

Async and Await keywords

Another way to prevent retain cycles is to use the async and await keywords. These keywords allow you to write asynchronous code that does not create retain cycles.

async func doSomething() {
  let viewController = ViewController()
  let collectionView = UICollectionView()

  // This closure does not create a retain cycle because it is captured using a weak reference.
  weak var weakViewController = viewController

  // This closure does not create a retain cycle because it is captured using an unowned reference.
  unowned var unownedCollectionView = collectionView

  // This code is asynchronous, so it does not create a retain cycle.
  await doSomethingAsync(viewController, collectionView)
}
However, these all can be forgotten while coding, and in PR reviews.

Here, we need to create a habit that will always check the common retain cycle possibilities in Automated tests:

When writing unit tests, to prevent common async bugs that come with retain cycles, we need to cover the memory leaks as well. We need to develop a habit that we need to check the memory leaks with every test so that we will be safe when we create async code blocks. And we need to add this check to every closure in every action that can create a new possible retain cycle.

2 ways we run those assertions after the test is passed:

  1. to use the tearDown method or,
  2. addTearDown {} this block runs after each test, just before tearDown is called.

So we can either add assertions to every test, which eventually will be a burden to add to each test and be harder to follow. Or we can come up with an easier solution that we can check the memory leak in every SUT(System Under Test) creation.

Since every test creates a SUT, the best way could be to create a factory method that makes the SUT, then in this factory we can also check the memory leak:
We need to run this assertion after the test has finished checking that SUT was deallocated from memory.

Note: If you are working with multiple objects in the test, make sure you are checking for all the coupled instances as well. Because usually we are focused to think about only the class that we are testing(SUT) and forget how the other collaborators of the class will react once the class has been deallocated.
For example: we have a viewcontroller, inside the viewcontroller there is this async block that has a strong reference to a collection view, even when the viewcontroller is dead, it still gets to be called because of the strong reference and eventually unexpected behaviors happen.

For addTearDown {} in the Apple documentation it says:

Registers a block to be run at the end of a test.
Teardown blocks are executed after the current test method has returned but before -tearDown is invoked.
Registered blocks are run on the main thread but can be registered from any thread. They are guaranteed to run only once, in LIFO order, and are executed serially. You may register blocks during -setUp, but you may not register blocks during -tearDown or from other teardown blocks.

addTearDown

The addTearDown block runs after each test, just before tearDown is called. This gives you a chance to check for memory leaks.

In the addTearDown block, you can use the XCTAssertNil assertion to check for memory leaks. The XCTAssertNil assertion asserts that the object passed to it is nil. If the object is not nil, the assertion will fail.

By using the addTearDown block, you can ensure that your automated tests do not create memory leaks.

Here is an example of how to use the addTearDown block to prevent a retain cycle:

func trackForMemoryLeaks() {
  let viewController = ViewController()

  let collectionView = UICollectionView()

  weak var weakViewController = viewController

  unowned var unownedCollectionView = collectionView

  doSomethingAsync(viewController, collectionView)

  // Add a tearDown block to check for memory leaks.
  addTearDown {
    XCTAssertNil(weakViewController)
    XCTAssertNil(unownedCollectionView)
  }
}



By using the addTearDown block, you can ensure that your automated tests do not create memory leaks.

It is important to check for memory leaks in all unit tests, not just those that use asynchronous code. This is because asynchronous code can create retain cycles that are not obvious.

We can also use the addTearDown block to check for memory leaks in other types of code, such as code that uses delegation or notifications.

By using the addTearDown block, we can help to ensure that our code is free of memory leaks.

Code Examples In Real Life:

3 responses to “Preventing Retain Cycles in Automated Tests Using addTearDown”

  1. kozhanovskidima Avatar
    kozhanovskidima

    interesting!

    Like

  2. […] • Preventing Retain Cycles in Automated Tests Using addTearDown […]

    Like

  3. […] хоста в жизнь на iOS• Миграция с CocoaPods на Tuist в Playtomic• Предотвращение циклов хранения при автоматизированно…• Приложение Pointfree SyncUps: отличный пример архитектуры […]

    Like

Leave a comment

HOME

Hope you’ll enjoy Swifty Posts!