You are on page 1of 132

Test-Driven Development

in Java

SPRINT3R
1 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
What is TDD ?

SPRINT3R
2 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Testing timeline
Kent Beck create SUnit Extreme Programming
1994 1999

1989 1995 2000


First test on punchcards Test-Driven Development Create JUnit

SPRINT3R
3 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Simple TDD Rules

Write a failing automated test before


you write any code

Remove duplication

SPRINT3R
4 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
TDD Cycle

SPRINT3R
5 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Improve TDD Cycle

Start with
THINK

SPRINT3R
6 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
วิธีการทําให้ Test Pass

Fake or Hard code


Triangulation
Use obvious implementation

SPRINT3R
7 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Why TDD ?

SPRINT3R
8 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
ทําไมต้อง TDD

เพื่อทําความเข้าใจกับปัญหาต่างๆ
คุณเข้าใจปัญหาหรือไม่ ?

SPRINT3R
9 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
ทําไมต้อง TDD

Software เปลี่ยนแปลงอยู่เสมอ
คนเรามักมโน และ ทําผิดพลาดอยู่เสมอ

SPRINT3R
10 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
ทําไมต้อง TDD

Test นั้นช่วยคุณสร้างสิ่งที่ นโม


และสร้างความคาดหวังขึ้นมา

SPRINT3R
11 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
ทําไมต้อง TDD

บอกคุณว่า ผิดตรงไหน
ไม่ต้องเสียเวลา debug

SPRINT3R
12 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
ทําไมต้อง TDD

ได้รับ feedback ที่รวดเร็ว

Many small changes vs One Big Change

SPRINT3R
13 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
ทําไมต้อง TDD

ให้ developer ทํางานเป็นทีมได้ดีขึ้น

SPRINT3R
14 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
TDD vs DLP
DLP = Debug Later Programming

รูปจาก http://www.renaissancesoftware.net/

SPRINT3R
15 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Remember :: Small step

SPRINT3R
Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Proactive vs Reactive

หาปัญหาก่อน
ทดสอบทีหลัง
ตรวจสอบ req ก่อนทํา
แก้ไขตาม defect ที่แจ้ง

ตรวจสอบ code
debug code

SPRINT3R
17 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Test ที่ดีต้อง FIRST

Fast
Independent
Repeatable
Self-validating
Timely

SPRINT3R
Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
สิ่งที่ไม่ใช่ Unit test

External service
File system
Database
WebService
API calls

SPRINT3R
Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
สิ่งที่ไม่ใช่ Unit test

Over specification
Compare screen image
Compare HTML

SPRINT3R
Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Workshop #1

Circular Buffer
http://en.wikipedia.org/wiki/Circular_buffer

SPRINT3R
21 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Workshop #2

Kata Range
http://codingdojo.org/cgi-bin/index.pl?KataRange

SPRINT3R
22 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
SPRINT3R
23 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Anatomy JUnit 4
Test Name

Annotation Test Case Name

SPRINT3R
24 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Test Naming

SPRINT3R
25 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
“What ’s in a name ?”
That which we call a rose

by any other name would smell


as sweet

Romeo and Juliet

SPRINT3R
26 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Guide to Test Writing

Don’t say “test”, say “should”

SPRINT3R
27 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Don’t use the word “test”

SPRINT3R
28 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Use the word “should”

SPRINT3R
29 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Guide to Test Writing

Don’t test your class, test behaviour

SPRINT3R
30 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Guide to Test Writing

Test class names are important too


Structure your test well
Tests are deliverable too

SPRINT3R
31 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Test Structure

SPRINT3R
32 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
The ratio of time spent (code)
versus writing is

over 10 to 1

Robert C. Martin, Clean Code

SPRINT3R
33 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Good Unit Test

Arrange
Act
Assert

SPRINT3R
34 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Setup Pattern

SPRINT3R
35 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Setup Pattern

Inline
Setup

SPRINT3R
36 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Setup Pattern

Delegate
Setup

SPRINT3R
37 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Setup Pattern

Implicit
Setup

SPRINT3R
38 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Implicit Teardown

SPRINT3R
39 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Ignore Testcase

SPRINT3R
40 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Handle Exception

SPRINT3R
41 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Traditional approach

SPRINT3R
42 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Expected Annotation

SPRINT3R
43 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
ExpectedException Rule

SPRINT3R
44 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Data-Driven with JUnit
@Parameterized

SPRINT3R
45 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Using parameterized

Set of test data


Expected result
Define test that uses the test data
Verify result against expected result

SPRINT3R
46 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Data-Driven Development

SPRINT3R
47 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Demo
@Parameterized
with Calculate Grade

Input Expected Result


80 A
70 B
60 C
50 D
40 F

SPRINT3R
48 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Step 1 :: Test Data

Input Expected Result


80 A
70 B
60 C
50 D
40 F

SPRINT3R
49 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Step 2 :: Add Runner

SPRINT3R
50 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Step 3 :: Matching fields

SPRINT3R
51 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Step 4 :: Define test case

SPRINT3R
52 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Step 5 :: Add @parameters

SPRINT3R
53 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Step 6 :: Test Result

SPRINT3R
54 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Workshop
@Parameterized
with Calculator

Operand 1 Operator Operand 2 Expected


1 + 1 2
2 + 2 4
1 - 1 0
2 - 1 1
3 * 1 3
3 * 2 6

SPRINT3R
55 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Workshop #3

Kata LCD Digit


http://cyber-dojo.org/

SPRINT3R
56 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Continuous Testing
with Infinitest
Support Eclipse IDE and IntelliJ
Run your tests in background when you save your code

https://infinitest.github.io/
SPRINT3R
57 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
TDD in Real world
Design Principle

SPRINT3R
58 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
SPRINT3R
59 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
SPRINT3R
60 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Single Responsibility Principle

Circle
+area()
+draw(UI)

SPRINT3R
61 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Circle
+area()

CircleUI
+draw(UI)

SPRINT3R
Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Workshop #4

Single Responsibility Principle

SPRINT3R
63 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
SPRINT3R
64 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Open/Closed Principle

DrawingEditor
+draw()
-drawCircle()
-drawRectangle()

SPRINT3R
65 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
DrawingEditor
+draw()

UIElement
+draw()

RectangleUI CircleUI
+draw() +draw()

SPRINT3R
Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Workshop #5

Open/Closed Principle

SPRINT3R
67 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
SPRINT3R
68 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
SPRINT3R
69 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
SPRINT3R
70 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Dependency Inversion Principle

SPRINT3R
71 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Dependency Inversion Principle

read
LogAnalyzer File

SPRINT3R
72 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Move to new class

LogAnalyzer FileExtension

read

File

SPRINT3R
73 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Workshop #6

Discuss about FileExtension.java

SPRINT3R
74 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Problem ?

Fast
Independent
Repeatable
Self-validating
Timely

SPRINT3R
75 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Dependency with File

LogAnalyzer FileExtension

read

File

SPRINT3R
76 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Discuss

How to Break dependency ?

SPRINT3R
77 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Isolate Dependency

Test Test target X Dependency

Test double

SPRINT3R
78 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Isolate Dependency

Test LogAnalyzer X FileExtension

Test double

SPRINT3R
79 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Test Double

Dummy Stub Spy Mock Fake

SPRINT3R
Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Test Double

Dummy Stub Spy Mock Fake

Configuration & Hard code

SPRINT3R
Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Stub

Class communication
Stub
Under Test

assert

Test

Make sure the test run smoothly

SPRINT3R
Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Mock object

Class communication
Mock
Under Test

assert

Test

To verify that the test pass

SPRINT3R
Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
How to use Test double ?

Test LogAnalyzer X FileExtension

Test double

SPRINT3R
84 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Demo & Discuss

Break dependency with File System

SPRINT3R
85 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Problem ?

SPRINT3R
86 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Problem ?

Code tight coupling !!

SPRINT3R
87 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Discuss

How to solve this problem ?

SPRINT3R
88 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
All about Abstraction
Tight coupling !!

LogAnalyzer FileExtension

SPRINT3R
89 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
All about Abstraction
Loose coupling !!

LogAnalyzer <<ExtensionService>>

FileExtension

SPRINT3R
90 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Demo & Discuss

Dependency Inversion Principle (DIP)

SPRINT3R
91 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
How to use FileExtension ?
Loose coupling

LogAnalyzer <<ExtensionService>>

FileExtension

SPRINT3R
92 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Dependency Injection

SPRINT3R
93 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Dependency Injection

Constructor Injection
Property Injection
Method Injection

SPRINT3R
94 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Demo & Discuss

Dependency Injection of LogAnalyzer

SPRINT3R
95 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Workshop
LogAnalyzer
FileExtension
+checkFileExtension(filename)
+analyze(filename) +isValid()

error

LogDatabase WebService
+save(message) +logError(message)

SPRINT3R
96 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Workshop #7

Stub with Database

SPRINT3R
97 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Discuss

About value of Stub database !!

SPRINT3R
98 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Problem

เมื่อทําการเปลี่ยน Schema database ?


ใช้ database จะมีคุณค่ากว่า stub ไหม ?

SPRINT3R
99 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Fake Database
with

SPRINT3R
100 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Create Clean Initial Perform
Dataset Database data tests

SPRINT3R
101 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Supported Database
Oracle
Hypersonic
Microsoft SQL Server H2
Sybase
PostgreSQL
MySQL
DB2 Informix
Derby

SPRINT3R
102 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
DBUnit Components
Database Connection
DataSet
DatabaseOperation
NONE REFRESH
INSERT TRUNCATE_TABLE
UPDATE CLEAN_INSERT = delete_all + insert
DELETE
DELETE_ALL

SPRINT3R
103 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
XML and POJO

SPRINT3R
104 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Workshop #8

Fake Database
with

SPRINT3R
105 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Step 1 :: Create dataset

Create Clean Initial Perform


Dataset Database data tests

SPRINT3R
106 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Database schema
schema_log.sql

SPRINT3R
107 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Database schema

SPRINT3R
108 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Dataset as XML
data_log.xml

SPRINT3R
109 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Step 2 :: Clean & Initial database

Create Clean Initial Perform


Dataset Database data tests

SPRINT3R
110 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Clean & Initial database

SPRINT3R
111 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Step 3 :: Perform test

Create Clean Initial Perform


Dataset Database data tests

SPRINT3R
112 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
How to perform test ?

SPRINT3R
113 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Create database connection

SPRINT3R
114 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Implement LogDAO

SPRINT3R
115 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Workshop
LogAnalyzer
FileExtension
+checkFileExtension(filename)
+analyze(filename) +isValid()

error

LogDatabase WebService
+save(message) +logError(message)

SPRINT3R
116 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Mock object

logError()
LogAnalyzer WebService

assert

Test

SPRINT3R
Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Workshop #9

Mock object with WebService

SPRINT3R
118 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Step 1 :: Break dependency

LogAnalyzer <<WebService>>

LogWebService

SPRINT3R
119 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Step 2 :: Write test

SPRINT3R
120 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Step 3 :: Write code

Start coding by your pair

SPRINT3R
121 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Workshop #10

Using Mock object and stub together

SPRINT3R
122 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Problem

logError()
LogAnalyzer WebService
sendEmail()

EmailService

SPRINT3R
Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Send Email with expected input ?

SPRINT3R
124 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Problem

logError()
LogAnalyzer WebService

sendEmail()

EmailService

assert

Test

SPRINT3R
Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Step 1 :: Break dependency

LogAnalyzer <<EmailService>>

LogEmailService

SPRINT3R
126 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Step 2 :: Write test

SPRINT3R
127 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Step 3 :: Write code

Start coding by your pair

SPRINT3R
128 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Mock Framework

SPRINT3R
129 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Mockito

Lightweight Java mocking


Enable mock creation
Enable verification
Enable stubbing

https://github.com/mockito/mockito
SPRINT3R
130 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
ฝึก ฝึก ฝึก และ ฝึก

SPRINT3R
131 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance
Resources

https://github.com/github-sprint3r/tdd-java-workshop

SPRINT3R
132 Siam Chamnankit Co., Ltd., Odd-e (Thailand) Co., Ltd. and Alliance

You might also like