✅ 이번 시간에는 테스트코드 리뷰 고고
🟠 UITest코드인데 BDD를 기반으로 작성한 코드도 있어
//
// MovieReviewUITests.swift
// MovieReviewUITests
//
// Created by Hamlit Jason on 2022/01/17.
//
import XCTest
// UITest의 경우에는 현업에서는 네트워크에 연결되어있어야 앱이 구동되는점, 또한, 여러번 클릭해서 그 지점으로 가서 사용해야하는 단점으로 인해 Unit테스트만 주로 사용한다.
class MovieReviewUITests: XCTestCase {
var app: XCUIApplication!
override func setUp() {
super.setUp()
continueAfterFailure = false // 실패하면 코드 쭉 실행안하고 종료
app = XCUIApplication()
app.launch()
}
override func tearDown() {
super.tearDown()
app = nil
}
func test_navigationBar의_타이틀이_영화평점으로_설정되어있다() {
let existNavigationBar = app.navigationBars["영화 평점"].exists
XCTAssertTrue(existNavigationBar)
}
func test_SearchBar가_존재한다() {
// 서치바가 네비게이션의 서치필드에 들어있기 때문에
let existSearchBar = app.navigationBars["영화 평점"]
.searchFields["Search"]
.exists
XCTAssertTrue(existSearchBar)
}
func test_SearchBar에_cancel버튼이_존재한다() {
let navigationBar = app.navigationBars
navigationBar
.searchFields["Search"]
.tap()
let existSearchBarCancelButton = navigationBar
.buttons["Cancel"]
.exists
XCTAssertTrue(existSearchBarCancelButton)
}
func test_녹화버튼을_클릭해서_테스트코드_작성() {
let app = XCUIApplication()
let navigationBar = app.navigationBars["영화 평점"]
let searchSearchField = navigationBar.searchFields["Search"]
searchSearchField.tap()
app.tables["Empty list"].swipeUp()
searchSearchField.swipeDown()
navigationBar.buttons["Cancel"].tap()
let collectionViewsQuery = app.collectionViews
collectionViewsQuery.children(matching: .cell).element(boundBy: 3).swipeUp()
collectionViewsQuery.children(matching: .cell).element(boundBy: 2).swipeDown()
}
// BDD
enum CellData: String {
case existsMovie = "셰도우 컨트리"
case notExistMovie = "007"
}
func test_영화가_즐겨찾기_되어있으면() {
let existsCell = app.collectionViews
.cells
.containing(.staticText, identifier: CellData.existsMovie.rawValue)
.element
.exists
XCTAssertTrue(existsCell, "Title이 표시된 셀이 존재한다")
}
func test_영화가_즐겨찾기_되어있지_않으면() {
let existsCell = app.collectionViews
.cells
.containing(.staticText, identifier: CellData.notExistMovie.rawValue)
.element
.exists
XCTAssertFalse(existsCell, "Title이 표시된 셀이 존재하지 않는다.")
}
}
enum으로 처리한게 특이점!
🟠 MockMovieSearchManager.swift
//
// MockMovieSearchManager.swift
// MovieReviewTests
//
// Created by Hamlit Jason on 2022/02/01.
//
import XCTest
@testable import MovieReview
final class MockMovieSearchManager: MovieSearchManagerProtocol {
var isCalledRequest = false
var needToSuccessRequest = false
// 핸들러는 리퀘스트 함수가 성공해야만 다룰 수 있음으로 이렇게 작성한다.
func request(
from keyword: String,
completionHandler: @escaping ([Movie]) -> Void
) {
isCalledRequest = true
if needToSuccessRequest {
completionHandler([])
}
}
}
핸들러를 처리하기 위해 needToSuccessRequest 변수를 따로 둔게 인상적이야.
🟠 MockMovieListViewController.swift
//
// MockMovieListViewController.swift
// MovieReviewTests
//
// Created by Hamlit Jason on 2022/02/01.
//
import XCTest
@testable import MovieReview
final class MockMovieListViewController: MovieListProtocol {
var isCalledSetupNavigationBar = false
var isCalledSetupSearchBar = false
var isCalledSetupViews = false
var isCalledUpdateSearchTableView = false
var isCalledPushToMovieViewController = false
var isCalledUpdateCollectionView = false
func setupNavigationBar() {
isCalledSetupNavigationBar = true
}
func setupSearchBar() {
isCalledSetupSearchBar = true
}
func setupViews() {
isCalledSetupViews = true
}
func updateSearchTableView(isHidden: Bool) {
isCalledUpdateSearchTableView = true
}
func pushToMovieViewController(with movie: Movie) {
isCalledPushToMovieViewController = true
}
func updateCollectionView() {
isCalledUpdateCollectionView = true
}
}
🟠 MockUserDefaultsManager.swift
//
// MockUserDefaultsManager.swift
// MovieReviewTests
//
// Created by Hamlit Jason on 2022/02/01.
//
import XCTest
@testable import MovieReview
final class MockUserDefaultsManager: UserDefaultsManagerProtocol {
var isCalledGetMovies = false
var isCalledAddMovie = false
var isCalledRemoveMovie = false
func getMovies() -> [Movie] {
isCalledGetMovies = true
return []
}
func addMovie(_ newValue: Movie) {
isCalledAddMovie = true
}
func removeMovie(_ value: Movie) {
isCalledRemoveMovie = true
}
}
여기까지가 MockList파일인데 특이한 점은 없고 그냥 쓱 보고 지나가자.
🟠 MovieListPresenterTest.swift
//
// MovieListPresenterTest.swift
// MovieReviewTests
//
// Created by Hamlit Jason on 2022/02/01.
//
import XCTest
@testable import MovieReview
class MovieListPresenterTest: XCTestCase {
var sut: MovieListPresenter!
var viewController: MockMovieListViewController!
var userDefaultManager: MockUserDefaultsManager!
var movieSearchManager: MockMovieSearchManager!
override func setUp() {
super.setUp()
viewController = MockMovieListViewController()
userDefaultManager = MockUserDefaultsManager()
movieSearchManager = MockMovieSearchManager()
sut = MovieListPresenter(
viewController: viewController,
movieSearchManager: movieSearchManager,
userDefaultsManager: userDefaultManager
)
}
override func tearDown() {
sut = nil
viewController = nil
userDefaultManager = nil
movieSearchManager = nil
super.tearDown()
}
// request 메소드가 성공하면 updateTableView가 실행
func test_serarchBar_textDidChanger가_호출될때_request가_성공하면() {
movieSearchManager.needToSuccessRequest = true
sut.searchBar(UISearchBar(), textDidChange: "")
XCTAssertTrue(viewController.isCalledUpdateSearchTableView, "updateSearchTableView가 실행된다.")
}
// request 메소드가 실패하면 updateTableView가 실행되지 않음
func test_serarchBar_textDidChanger가_호출될때_request가_실패하면() {
movieSearchManager.needToSuccessRequest = false
sut.searchBar(UISearchBar(), textDidChange: "")
XCTAssertFalse(viewController.isCalledUpdateSearchTableView, "updateSearchTableView가 실행되지 않는다.")
}
func test_viewDidLoad가_호출되면() {
sut.viewDidLoad()
XCTAssertTrue(viewController.isCalledSetupNavigationBar)
XCTAssertTrue(viewController.isCalledSetupSearchBar)
XCTAssertTrue(viewController.isCalledSetupViews)
}
func test_viewWillAppear가_호출되면() {
sut.viewWillAppear()
XCTAssertTrue(userDefaultManager.isCalledGetMovies)
XCTAssertTrue(viewController.isCalledUpdateCollectionView)
}
func test_searchBarDidBeginEditing이_호출되면() {
sut.searchBarTextDidBeginEditing(UISearchBar())
XCTAssertTrue(viewController.isCalledUpdateSearchTableView)
}
func text_searchBarCancelButton이_호출되면() {
sut.searchBarCancelButtonClicked(UISearchBar())
XCTAssertTrue(viewController.isCalledUpdateSearchTableView)
}
}
// Cuckoo 테스트 파일을 자동으로 만들어주는 외부 프레임워크도 존재
Cuckoo라는 외부 프레임워크도 있다는데 이것도 조사해보자
또한 핸들러를 처리하기 위한 변수를 이용하는 것이 인상적인데 이도 공부해보자
🟠 MovieDetailPresenterTest.swift
//
// File.swift
// MovieDetailPresenterTest
//
// Created by Hamlit Jason on 2022/02/01.
//
import XCTest
@testable import MovieReview
class MovieDetailPresenterTest: XCTestCase {
var sut: MovieDetailPresenter!
var viewController: MockMovieDetailViewController!
var movie: Movie!
var userDefaultManager: MockUserDefaultsManager!
override func setUp() {
super.setUp()
viewController = MockMovieDetailViewController()
movie = Movie(title: "", imageURL: "", userRating: "", actor: "", director: "", pubDate: "")
userDefaultManager = MockUserDefaultsManager()
sut = MovieDetailPresenter(
viewController: viewController,
movie: movie,
userDefaultsManager: userDefaultManager
)
}
override func tearDown() {
sut = nil
viewController = nil
movie = nil
userDefaultManager = nil
super.tearDown()
}
func test_viewDidLoad() {
sut.viewDidLoad()
XCTAssertTrue(viewController.isCalledSetViews)
XCTAssertTrue(viewController.isCalledSetRightBarButton)
}
func test_didTapRightBarButtonItem이_호출될때_isLiked가_true가되면() {
movie.isLiked = false
sut = MovieDetailPresenter(
viewController: viewController,
movie: movie,
userDefaultsManager: userDefaultManager
)
sut.didTapRightBarButtonItem()
XCTAssertTrue(userDefaultManager.isCalledAddMovie)
XCTAssertFalse(userDefaultManager.isCalledRemoveMovie)
XCTAssertTrue(viewController.isCalledSetRightBarButton)
}
func test_didTapRightBarButtonItem이_호출될때_isLiked가_false가되면() {
movie.isLiked = true
sut = MovieDetailPresenter(
viewController: viewController,
movie: movie,
userDefaultsManager: userDefaultManager
)
sut.didTapRightBarButtonItem()
XCTAssertFalse(userDefaultManager.isCalledAddMovie)
XCTAssertTrue(userDefaultManager.isCalledRemoveMovie)
XCTAssertTrue(viewController.isCalledSetRightBarButton)
}
}
movie 데이터를 직접 주기위한 처리방법을 여기서 사용하였다. 이 부분이 상당히 인상적인데, 한번 같이 알아보자.
sut에 movie값을 새로 할당해주는데, 이를 프로토콜로 처음에 작성할 때 만들었기에 이게 가능하다고 보인다.
또한 toggle을 사용했기에 isLiked를 반대로 주어야 didTapRight메소드가 실행되면 toggle을 적용받아 원하는 boolean값을 갖게 되어서 흐름을 처리할 수 있다.
데이터를 넣어주는 것에 주목하자
'Archive > 패캠(초격차)' 카테고리의 다른 글
part5 (ch6). KeywordNews 코드리뷰 (0) | 2022.02.15 |
---|---|
part5 (ch6). 🪛 CI/CD란? (feat. bitrise) (0) | 2022.02.15 |
part5 (ch6). BDD의 개념에 대해서 알아보기 (0) | 2022.02.01 |
part5 (ch6) UI Test (0) | 2022.02.01 |
part5 (ch6). MovieReview 코드리뷰 (0) | 2022.01.31 |