티스토리 뷰
VBA Rubberducking (Part 3)
이 글은 Rubberduck Blog의 VBA Rubberducking (Part 3) 글을 번역하며 공부하기 위한 기록으로, 일부 생략된 부분이 있을 수 있습니다. 오역, 오타에 관한 자유로운 의견 감사드립니다.
이 포스팅은 오픈소스 VBE 애드인 Rubberduck의 다양한 기능을 안내하는 시리즈 중 세 번째 게시물입니다.
첫 번째 게시물은
탐색
기능에 관한 것이었습니다.두 번째 게시물은
코드 검사기
의 내용입니다.
단위 테스트 (Unit Testing)
Rubberduck을 예전부터 아신 분이라면, 프로젝트가 어디에서 왜 시작되었는지 알고 계실 것입니다. Rubberduck은 VBE add-in이 되기 전에는, 이 코드 리뷰 글로 시작되어 VBA로 작성된 Excel add-in이었습니다. "Rubberduck"이라는 이름이 되기도 전에, 이 VBA 코드의 C# Port였습니다. 여러 추가 프로젝트에서 모든 코드를 복사할 필요 없이 Excel, Access 및 Word VBA에서 단위 테스트를 작성하며 실행할 수 있는 것이 목표입니다.
제로 보일러플레이트 (Zero Boilerplate)
여기에 기존과 다른 VBA 단위 테스트가 있습니다. 보통 많은 양의 상용구(Boilerplate) 설정 코드가 필요합니다. 위에서 언급한 VBA로 작성된 프로젝트는 VBIDE 개체 모델에 대한 프로그래밍 접근이 필요하여, 보안 우려가 있습니다. (VBA가 VBA 코드를 생성하고 실행하는 코드를 실행하도록 허용하는 것입니다.)
Rubberduck 단위 테스트는 둘 다 필요하지 않습니다. Rubberduck은 이미 VBE add-in이며, IDE의 코드에 프로그래밍 방식으로 액세스할 수 있고, 기업 보안 정책을 위배하지 않으면서 VBA 코드를 스캔, 수정, 생성 및 실행할 수 있기 때문입니다.
Rubberduck은 보일러플레이트가 거의 필요 없습니다. 다음은 완전히 작동하는 테스트 모듈입니다:
'@TestModule
Private Assert As Rubberduck.AssertClass
'@TestMethod
Public Sub FooIs42()
'Arrange
Const expected As Integer = 42
Dim actual As Integer
'Act
actual = Module1.GetFoo
'Assert
Assert.AreEqual expected, actual, "Nope, not 42."
End Sub
네, 그냥 예시이지만 작동하는 데에 필요한 것이 얼마나 적은지 보여줍니다.
@TestModule
주석은 표준 모듈의 선언 섹션에 있습니다.Rubberduck.AssertClass
인스턴스는, 초기 혹은 후기 바인딩(late or early-bound)될 수 있습니다.@TestMethod
주석은 테스트 메소드를 공식적으로 식별합니다.
@TestMethod
는 2.0 버전 이상에서 필수 주석입니다. 테스트할 메소드는 Public이어야 하고 매개변수가 없어야 하며, 표준 모듈에 있어야 합니다.

GetFoo는 42를 반환하는 것이 비즈니스 요구 사항이고, Module1 혹은 다른 곳에서의 수정사항으로 실수로 인해 GetFoo가 0을 반환한다고 가정해봅니다. 만약 비즈니스 요구 사항을 문서화하고 검증하는 단위 테스트가 없는 경우, 발견하는 데에 시간이 걸릴 수 있는 버그를 도입하였습니다. 반대로 비즈니스 요구 사항 충족 여부를 확인하기 위해 변경할 때마다 단위 테스트를 실행하는 습관을 들였다면...

테스트에 실패하고, 조만간 버그로 보고될 동작의 변화가 미묘하게 도입됨을 즉시 알아차릴 수 있습니다.
추상화에 대한 코드 (Code Against Abstractions)
어떤 언어가 되었든, 코드가 UI, 파일시스템, 웹 서비스, 데이터베이스, 워크시트 등에 밀접하게 연결되어있고 액세스하는 경우에는 적합하지 않습니다.
왜냐하면 단위 테스트는
- 빨라야 합니다.
- 부작용이 없어야 합니다.
- 다른 테스트에 의존하거나 영향을 주면 안됩니다.
- 모든 종속성을 제어해야 합니다.
- 한 가지의 테스트와, 한 가지의 실패 이유만이 있어야 합니다.
잠깐, 코드가 워크시트에 접근하면 테스트를 할 수 없는 건가요?
맞으면서 아닙니다. Code Review의 많은 게시물에서 대용량 데이터를 빠르게 실행하는 팁을 요청합니다. 필자는 워크시트 액세스가 VBA에서 할 수 있는 가장 느린 작업이라고 자주 말한다고 합니다.
워크시트에 대한 논리 코딩보다는 워크시트의 추상화에 대한 코딩을 진행하세요. 배열이면 충분한 경우가 많습니다. 워크시트가 아닌 배열로 작동되게 리팩터링(refactor your logic)하면 테스트 작업도 가능하면서, 성능도 향상됩니다.
로직을 클래스 모듈에 캡슐화하고 공용 인터페이스를 테스트하세요. 로직이 UI(메시지 상자)까지 표시하면, 해당 코드를 다른 곳으로 분리합니다. 코드 책임을 분리하고, 중요한 곳에 테스트를 집중합니다.
코드의 결합력 감소, 응집력 증가, 일반적인 테스트 작성에 관해서 책 한 권으로 쓰여질 수 있습니다. 주변을 캐내어, 연구해보세요. Rubberduck은 사용자의 VBA 코드 위치를 확인할 수 있습니다.
테스트 탐색기 (The Test Explorer)
Rubberduck의 테스트 탐색기는 두 가지의 명령 "세트"를 제공합니다: 바로 "실행(Run)"과 "추가(Add)"입니다.
"추가" 메뉴는 테스트 모듈 (test module) 을 프로젝트에서 쉽게 추가하도록 하며, 다음의 두 템플릿 중 하나인 테스트 메소드 (test method) 를 쉽게 추가할 수 있습니다.
- 테스트 메소드 / Test Method 는 일반적인 Arrange-Act-Assert로, 에러 핸들링이 테스트를 보장하여 정확한 실패를 보장하고 오류를 알려줍니다.
- 테스트 메소드 (예상 오류) / Test Method (Expected Error) 템플릿은 AAA와 같지만, 런타임 오류를 일으킬 것으로 예상되는 테스트를 작성하기 위한 것으로 이런 테스트는 예상 오류가 발생하지 않으면 실패합니다.
"실행" 메뉴는 테스트 전부, 혹은 일부를 쉽게 실행할 수 있습니다. 예) 마지막으로 실행했을 때 실패한 테스트만 실행할 수도 있습니다.
결과는 결과별이나 지역별(프로젝트/모듈)로 그룹화할 수 있으며, 클릭 한 번으로 다시 클립보드로 복사될 수 있습니다.
테스트 설정을 통해 테스트 모듈 템플릿의 내용을 제어할 수 있습니다.

바인딩 방식 (binding mode) 은 AssertClass 인스턴스가 "As Object" (후기바인딩, 기본값)로 선언될 것인지 혹은 "As New Rubberck.AssertClass" (초기-바인딩)으로 선언될 것인지 결정합니다.
"보안 종류" (Type safty) 는 Assert 변수가 Rubberduck.AssertClass(제한, strict)인지 Rubberduck.PermissiveAssertClass(제한, permissive)인지를 결정합니다.
the permissive asserts differs with the strict (original and default) version in that equality checks are more closely modeled on VBA equality rules: with a permissive assert, an Integer value of 254 can be compared to a Byte value of 254 and deemed equal. Strict equality requires the types to match, not just the value.
테스트 모듈 템플릿 (Test Module Template) 체크박스는 @TestInitialize, @TestCleanup, @ModuleInitialize and @ModuleCleanup 메소드 스텁이 생성된 후, 새 테스트 모듈을 생성할 때 테스트 메서드를 기본으로 생성할지 결정합니다.
이런 모든 설정은 기존 모듈이 아닌, 새로운 테스트 모듈에만 영향을 줍니다.
Assert 클래스 (The Assert Class)
assert로 테스트 하세요. assertion 없이는 Rubberduck 테스트는 의미있는 결과를 낼 수 없으며, 간단히 통과할 것입니다. IAssert 인터페이스(AssertClass와 PermissiveAssertClass 모두 상속된)는 Visual Strudio의 MS-테스트에서 크게 영감을 받은 수많은 멤버를 공개합니다.
Name | Description |
---|---|
AreEqual | 지정된 두 개체가 동일한지 검증합니다. 개체가 동일하지 않으면 assertion은 실패합니다. Verifies that two specified objects are equal. The assertion fails if the objects are not equal. |
AreNotEqual | 지정된 두 개체가 동일하지 않은지 검증합니다. 개체가 동일하면 assertion은 실패합니다. Verifies that two specified objects are not equal. The assertion fails if the objects are equal. |
AreNotSame | 지정된 두 개체 변수가 서로 다른 개체를 참조하는지 검증합니다. 만약 같은 개체를 참조하면 assertion은 실패합니다. Verifies that two specified object variables refer to different objects. The assertion fails if they refer to the same object. |
AreSame | 지정된 두 개체가 같은 개체를 참조하는지 검증합니다. 만약 서로 다른 개체를 참조하면 assertion은 실패합니다. Verifies that two specified object variables refer to the same object. The assertion fails if they refer to different objects. |
Fail | 어떤 조건도 확인하지 않고 assertion은 실패합니다. Fails the assertion without checking any conditions. |
Inconclusive | assertion을 검증할 수 없음을 나타냅니다. Indicates that the assertion cannot be verified. |
IsFalse | 지정된 조건이 false인지 검증합니다. 조건이 true이면 assetion이 실패합니다. Verifies that the specified condition is false. The assertion fails if the condition is true. |
IsNothing | 지정된 개체가 Nothing인지 검증합니다. not Nothing이면 assertion이 실패합니다. Verifies that the specified object is Nothing. The assertion fails if it is notNothing. |
IsNotNothing | 지정된 개체가 not Nothing인지 검증합니다. 만약 Nothing이면 assertion이 실패합니다. Verifies that the specified object is not Nothing. The assertion fails if it isNothing. |
IsTrue | 지정된 조건이 true인지 검증합니다. 조건이 false이면 assetion이 실패합니다. Verifies that the specified condition is true. The assertion fails if the condition is false. |
이어지는글
'VBA > RubberDuck' 카테고리의 다른 글
OOP in VBA: Immutability & The Factory Pattern (1) | 2022.09.20 |
---|---|
VBA Rubberducking (Part 4) (0) | 2022.05.18 |
VBA Rubberducking (Part 2) (0) | 2022.05.09 |
VBA Rubberducking (Part 1) (0) | 2022.05.01 |
VBA Rubberduck / 러버덕 (0) | 2022.05.01 |