-
Nimble - Behavior Driven DevelopmentLibrary 2025. 4. 12. 06:36
안녕하세요. 그린입니다 🍏
이번 포스팅에서는 저번 Quick에 이은 Behavior Driven Development 2탄인 Nimble입니다 🙋🏻
사실, Nimble부터하고 Quick을 하는게 맞나 싶긴했지만 뭐 서로 연관이 있을뿐 뭐가 먼저다 그런건 없으니 괜찮을거라 생각해요!
그럼 바로 알아볼까요?
Nimble?
XCTest만으로 테스트 코드를 작성하다보면 많은 분들이 가독성이 떨어진다고 느낄때가 많아요.
비교하는 표현 자체가 제한적이여서 그럴 수 있죠.
XCTAssertEqual, XCTAssertTrue, XCTAssetNil과 같은 메서드들은 기능으로는 잘 동작하지만 표현이 제한적이기에 BDD 스타일에서는 조금 어려울 수 있습니다.
즉, 명확하게 사용자 행동 양식의 테스트 의도를 나타내기 어려워요 🥲
그래서, 이런것들을 Nimble이 보완해줄 수 있습니다.
Quick과 함께 사용하면 BDD를 한층 더 강화해주는 테스트 코드를 구성할 수 있지만 Nimble 단독적으로도 충분합니다.
그럼 실제로 어떻게 사용되는지 예시들로 봐볼까요?
Usage Nimble
Nimble의 핵심은 가독성에 있습니다.
아래를 볼까요?
expect(actual).to(equal(expected)) expect(value).to(beNil()) expect(array).to(contain(10)) expect(name).toNot(beginWith("GREEN"))
즉, 우리가 쓰는 자연어에 가까운 테스트 구문을 제공해줘서 해당 테스트의 목적성을 확실하고 쉽게 파악할 수 있어요.
실제 테스트 코드 파일에서 한번 구성해볼까요?
import XCTest import Nimble final class MathTests: XCTestCase { func testAddition() { let result = 1 + 1 expect(result).to(equal(2)) } }
이렇게 연산을 비교하는 메서드를 짤때 간단하게 자연어처럼 구성할 수 있어요.
Quick과 사용하지 않아도 이렇게 XCTest만으로도 사용이 가능하죠.
func testOptionalValue() { let name: String? = "Green" expect(name).toNot(beNil()) } func testArrayContainsValue() { let numbers = [1, 2, 3, 4, 5] expect(numbers).to(contain(3)) expect(numbers).toNot(contain(6)) }
이렇게 다양한 타입과 테스트에 대해서도 풍부하고 쉬운 표현이 가능합니다.
조금 더 나아가 비동기 테스트에 대해서는 어떻게 가독성있게 구성될까요?
func testAsyncValue() { var value = 0 DispatchQueue.global().asyncAfter(deadline: .now() + 1) { value = 10 } expect(value).toEventually(equal(10), timeout: .seconds(2)) }
이렇게 toEventually 메서드를 통해 비동기 테스트 시에 대한 결과도 검증해볼 수 있습니다.
enum CustomError: Error { case somethingWrong } func throwError() throws { throw CustomError.somethingWrong } func testThrowsError() { expect { try throwError() }.to(throwError()) }
예외 처리 부분에서도 이렇게 쉽게 테스트 코드를 작성하고 기대 결과를 받아볼 수 있죠.
한단계 조금 더 나아가서 실제 간단한 MVVM 구조에서 어떻게 Nimble을 활용해서 테스트 코드가 작성되는지 보겠습니다.
class UserViewModel { var userName: String = "" func updateName(to newName: String) { userName = newName } } final class UserViewModelTests: XCTestCase { func testUserNameUpdate() { let viewModel = UserViewModel() viewModel.updateName(to: "Nimble") expect(viewModel.userName).to(equal("Nimble")) } }
아주 간단한 뷰모델을 테스트하지만 테스트 코드에서 가독성은 훨씬 증가했죠.
XCTest만으로 진행했다면 아마 이렇게 테스트를 했을거에요.
final class UserViewModelTests: XCTestCase { func testUserNameUpdate() { let viewModel = UserViewModel() viewModel.updateName(to: "Nimble") XCTAssertEqual(viewModel.userName, "Nimble") } }
물론 지금 이건 아주 간단한 테스트 코드라서 이것도 가독성이 있다고 볼 수 있지만, Nimble의 expect 구문을 사용하는것보다는 자연스럽지 않다고 느껴집니다.
이런 부분은 특히 테스트 케이스가 많아지고 복잡해질수록 높은 가독성이 얼마나 중요한지 체감이 되는 부분이죠 😁
마지막으로 그러면 Nimble에서 편리한 Matcher들을 한번 살펴볼께요 🙋🏻
Matcher Description equal(value) 동일 값 체크 beNil() nil 체크 contain(element) Array 포함 체크 beTrue() / beFalse() Bool 체크 beginWith(prefix) 문자열 prefix 체크 endWith(suffix) 문자열 suffix 체크 throwError() 예외 발생 여부 요정도를 가장 많이 활용하게 될거에요.
Summary
Nimble은 사실 단순하게 가독성이 좋은 테스트 코드를 제공할 수 있다를 넘어서서 유지보수성을 높여주는데 일조합니다.
특히나 복잡한 비지니스 로직을 가졌다면 이런 가독성이 좋고 명확한 표현 자체가 디버깅에 도움을 주겠죠?
또한, BDD를 따름에 있어 더 쉽게 맞춰 표현할 수 있다고 느껴집니다.
Reference
GitHub - Quick/Nimble: A Matcher Framework for Swift and Objective-C
A Matcher Framework for Swift and Objective-C. Contribute to Quick/Nimble development by creating an account on GitHub.
github.com
'Library' 카테고리의 다른 글
Quick - Behavior Driven Development (1) 2025.04.05 YouTubePlayerKit을 활용한 쇼츠 구현하기 (91) 2024.08.01 Firebase - Remote Config (67) 2024.03.11 Get 라이브러리로 심플한 웹 API 클라이언트 구현하기 (4) 2023.02.13 PopupView (6) 2023.02.06