1 of 41

�Test dan SOLID

Disarikan dari berbagai sumber oleh Ade Azurat untuk

PMPL 2023 Fasilkom UI

dan

Internal Training Pusilkom UI

1

2 of 41

Review

  • Effort on conducting test (not TDD)
    • Unit test, as part of programmer’s tasks, it should be easy! And it should be fast! It should not consume much time and it will save a lot of programmer’s time in debugging and thinking about what s/he has to build. But they should familiarize themselves with unit test.
    • Integration test, can be build by some specific programmers, or lead programmer. It is part of their tasks to guarantee it teamwork’s.
    • Other tests are supposed to be developed by QA Team.
    • Tests by QA Team are supposed to be managerial obligation to allocate it.

2

3 of 41

Review

  • Test slices: Spring Boot Test Slices.
  • Test Slice in java Spring boot, �will improve the integration test speed performance. So, it would only �be effective for those team who �already use to do integration test!
  • Starting point for test, start from:
    • the left shift (unit test) or
    • the right shift (BDD Test or Functional Test)

3

4 of 41

Outline

  • SOLID
  • Test Double
  • Mock and Stub
  • FIRST

4

5 of 41

SOLID dan OO Design

5

6 of 41

SOLID dan OO Design

  • Pada dasarnya SOLID adalah formulasi dari Object Oriented Design Principles
    1. DRY (Don't repeat yourself)
    2. Encapsulate What Changes
    3. Open Closed Design Principle
    4. Single Responsibility Principle (SRP)
    5. Dependency Injection or Inversion principle
    6. Favor Composition over Inheritance
    7. Liskov Substitution Principle (LSP)
    8. Interface Segregation Principle (ISP)
    9. Programming for Interface not implementation
    10. Delegation principles

6

7 of 41

S.O.L.I.D

Awal 2000an Robert C. Martin mempopulerkan SOLID sebagai prinsip untuk software developer

    • S = Single Responsibility Principles
    • O = Open/ Close Principles
    • L = Liskov Substitution Principles
    • I = Interface Segregation Principles
    • D = Dependency Inversion Principles

7

8 of 41

Single Responsibility Principles

  • Setiap class dan method sepantasnya hanya memiliki satu tugas/tujuan.
  • Dengan demikian setiap test yang fail terhadap class/method tersebut, menyatakan bahwa ada satu fungsi/behavior yang gagal.
  • Method dan Class akan cenderung dibuat lebih kecil (fine grain) hal ini akan mempercepat progress pengembangan. Membuat test jadi cepat, demikian juga membuat implementasi jadi lebih cepat.
  • Butuh disiplin untuk konsisten melakukan hal ini.
  • Ingat: not code for today, but code for later!

8

9 of 41

Open/ Close Principles

  • Sebuah class terbuka (open) untuk bisa di extend. Namun tertutup (close) untuk perubahan.
  • Dengan perancangan yang sudah memikirkan OCP, maka akan menjamin bahwa core (base class) tidak diubah, maka menghindari peluang refactoring test.
  • Akan terkait juga dengan isu Dependency Inversion, yang akan dibahas.

9

10 of 41

Liskov Substitution Principle

  • Sebuah obyek perlu bisa digantikan oleh instance dari sub-class nya dengan baik dalam arti tidak merusak behavior dari aplikasi.
  • Misal: objek Binatang, selalu bisa digantikan dengan obyek Kucing tanpa mengganggu behaviour dari Binatang.
  • Contoh yang tidak mengikuti LSP, adalah Parent class Persegi_Panjang memiliki sub-class Bujursangkar. Behavior menghitung luas dan lingkaran akan berbeda.
  • Dengan penerapan LSP, code test yang dibuat akan lebih isolated. Lebih terlihat lagi dengan isu Dependency Inversion

10

11 of 41

Interface Segregation Principle

  • Penerapan Interface yang tepat dan tidak berlebihan. Interface dibuat relatif terpisah, kecil (smaller) sesuai dengan kebutuhannya.
  • Perlu membiasakan untuk membuat interface untuk class yang relatif mungkin akan diimplementasikan berbeda.
  • Dengan interface yang specific dan kecil-kecil, pembuatan test akan lebih specifik juga, tidak rumit dan lebih mudah dipahami.

11

12 of 41

Dependency Inversion Principle

  • Dependency Inversion muncul dari perkembangan software yang semakin kompleks.
  • Software yang besar (kompleks) cenderung memiliki keterkaitan dan keterikatan antara satu modul dengan modul lain. Hal ini akan menyulitkan untuk maintenance.
  • Dengan penerapan Dependency Inversion Principle (DIP), kita akan cenderung dipacu agar bergantung pada abstraksi (interface) bukan pada implementasi. (Code for an interface)

12

13 of 41

Dependency Injection/Inversion?

Dependency Injection

Dependency Inversion

  • Dependency Injection adalah methodology untuk mengupayakan tercapainya Dependency Inversion

13

14 of 41

Dependency Injection

  • Dependency Injection adalah hal penting dalam penerapan TDD yang baik.
  • Dengan DI, akan memungkinkan kita membuat mock dengan tepat agar dapat melakukan test secara isolated.
  • Tersedia juga framework untuk memudahkan penerapan Dependency injection seperti Google Guice, Spring, Dagger, dll.

14

15 of 41

Example

public class BusinessService {

private SqlDataStoreProvider _dataStoreProvider = new SqlDataStoreProvider();

private DbLoggingProvider _loggingProvider = new DbLoggingProvider();

private ProdWebServiceProvider _webServiceProvider = � new ProdWebServiceProvider();

}�

15

16 of 41

Isu?

  • Class yang dibuat terikat dengan instantiasi kelas lain.
  • Untuk menguji class BusinessService, kita harus menyertakan implementasi dari ketiga class yang dibutuhkan.
  • Padahal ketiga kelas yang dibutuhkan dari external bagian yang tidak perlu di-uji saat unit test.
  • Kita akan kesulitan untuk melakukan mocking.

16

17 of 41

Example

public class BusinessService {

private DataStoreProvider _dataStoreProvider; �private LoggingProvider _loggingProvider; �private WebServiceProvider _webServiceProvider;

public BusinessService(DataStoreProvider dataStoreProvider, LoggingProvider loggingProvider,� WebServiceProvider webServiceProvider) {

_dataStoreProvider = dataStoreProvider; �_loggingProvider = loggingProvider; �_webServiceProvider = webServiceProvider;

}�}

17

18 of 41

Improvement lain?!

public class BusinessService {

private DataStoreProvider _dataStoreProvider; �private LoggingProvider _loggingProvider; �private WebServiceProvider _webServiceProvider;

public BusinessService(DataStoreProvider dataStoreProvider, LoggingProvider loggingProvider,� WebServiceProvider webServiceProvider) {

_dataStoreProvider = dataStoreProvider; �_loggingProvider = loggingProvider; �_webServiceProvider = webServiceProvider;

}�}

18

19 of 41

Isu?

  • Contoh tersebut memungkinkan kita melakukan unit test terhadap class BusinessService dengan me-mock database, logging dan webservice.
  • Bila kita menerapkan prinsip Programming for Interface, maka kita bisa membuat lebih fleksibile lagi dengan menyertakan Interface sebagai parameter.
  • Dengan ini kita juga tidak perlu memiliki implementasi dari Base class untuk melakukan compilasi.

19

20 of 41

Example

public class BusinessService {

private IDataStoreProvider _dataStoreProvider; �private ILoggingProvider _loggingProvider; �private IWebServiceProvider _webServiceProvider;

public BusinessService(IDataStoreProvider dataStoreProvider, ILoggingProvider loggingProvider,� IWebServiceProvider webServiceProvider) {

_dataStoreProvider = dataStoreProvider; �_loggingProvider = loggingProvider; �_webServiceProvider = webServiceProvider;

}�}

20

21 of 41

Isu?

  • Dengan penerapan DI, kita telah membuat dependensi menjadi loosely coupled.
    1. Kita telah mengatur dependensi ke abstraction
    2. kita bisa menunda penggunaan concrete instance sampai benar-benar dibutuhkan.
  • Ini hal positif, namun kita belum selesai.
  • Bila hanya demikian, maka ada prinsip SOLID yang kita langgar!

21

22 of 41

Pelanggaran SRP

  • Kita masih memberikan ketergantungan ‘pengetahuan’ untuk bisa melakukan instantiasi terhadap parameter yang dibutuhkan.
  • SRP menyatakan bahwa merupakan tanggung jawab sebuah class untuk mengurus dirinya.
  • Misalnya, kita tidak sepantasnya menggunakan mock pada database, tapi menginstantiasi dengan concrete object pada logging.
  • Parent class (BusinessService) yang bertanggung jawab membuat instantiasi.

22

23 of 41

Software Factories

public class BusinessServiceFactory {

public static BusinessService GetNewBusinessService() {

dataStoreProvider = GetNewDataStoreProvider();

loggingProvider = GetNewLoggingProvider();

webServiceProvider = GetNewWebServiceProvider();

� return new BusinessService(dataStoreProvider, loggingProvider,

webServiceProvider);

}

private static IWebServiceProvider GetNewWebServiceProvider(){

return new WebServiceProvider();

}

private static ILoggingProvider GetNewLoggingProvider(){

return new LoggingProvider();

}

private static IDataStoreProvider GetNewDataStoreProvider(){

return new DataStoreProvider();

}

}

23

24 of 41

Factory & DI Framework

  • Software Factory cukup efektif, namun tidak ideal.
  • Pengaturan Software Factory membutuhkan maintenance. Bila aplikasi berkembang, perlu membuat terus menerus, berulang, dan juga mungkin tertukar/keliru (error prone)
  • Dependency Injection Framework bekerja sebagaimana software factory dengan penambahan aturan yang dapat menghindari kesalahan.
  • Kita bisa menetapkan binding rule, misalnya.

24

25 of 41

DI Framework

  • Secara umum dalam konfigurasi DI, ada 4 bagian
    1. The Application – Aplikasi yang kita buat
    2. The Kernel or the Container – Interface yang dibuat pada aplikasi.
    3. The Provider – Kumpulan aturan untuk penyusunan berikut dependensinya
    4. The Created Class – Instance dari class yang akan dihasilkan dari DI Framework

25

26 of 41

Test Double

  1. Dummy: Object dibuat namun tidak digunakan. Sekedar untuk pengisi data parameter.
  2. Fake: Ada concrete implementation namun tidak digunakan untuk opeasional, misal InMemoryDatabase
  3. Stubs: Preprogrammed untuk memberikan hasil yang diinginkan saja, tapi tidak memiliki implementasi dan tidak memberikan hasil selain yang diminta.
  4. Spies: Seperti stubs, namun juga menyimpan record untuk kebutuhan test. Namun tidak mengubah behavior, hanya monitoring.
  5. Mocks: Preprogrammed untuk memberikan hasil sesuai spesifikasi dengan tambahan monitoring atau side effect. Termasuk throw exception dan juga menyimpan record untuk kebutuhan test.

26

27 of 41

Spies

  • Use Spies to:
    • test legacy code collaborators that are too difficult or impossible to test with a Mock or Spy
    • verify the collaborator's method is called the correct number of times
    • verify the collaborator's method is called with the correct parameters
    • provide a predetermined response from a collaborator
    • take a predetermined action from a collaborator, like throwing an exception

27

28 of 41

Stubs

  • Use Stubs to:
    • provide a predetermined response from a collaborator
    • take a predetermined action from a collaborator, like throwing an exception

28

29 of 41

Mocks

  • Use Mocks to:
    • verify the contract between the code under test and a collaborator
    • verify the the collaborator's method is called the correct number of times
    • verify the collaborator's method is called with the correct parameters

29

30 of 41

Stubbing and Mocking

  • Dalam banyak test framework, disatukan dalam istilah mock.
  • Secara programming tidak terlalu terlihat perbedaannya.
  • Secara konseptual perlu dipahami perbedaan nya.

30

31 of 41

Customer

@Entity

public class Customer {

@Id

@GeneratedValue(strategy=GenerationType.AUTO)

private long id;

private String firstName;

private String lastName;

//...getters and setters redacted for brevity...

}

31

32 of 41

CustomerReader

public class CustomerReader {

@PersistenceContext

private EntityManager entityManager;

public String findFullName(Long customerID){

Customer customer =

entityManager.find(Customer.class,

customerID);

return customer.getFirstName()

+" "+customer.getLastName();

}

}

32

33 of 41

33

34 of 41

Stubbing

  • Contoh menggunakan Mockita
  • stubbing menggunakan pola:

when(something).thenReturn(somethingElse) 

  • Perhatikan bahwa contoh tadi belum menerapkan test dengan baik, belum ada negatif test. (lihat sumber tutorial di akhir slide untuk lebih lengkapnya)
  • Selanjutnya contoh melakukan mocking

34

35 of 41

35

36 of 41

36

37 of 41

37

38 of 41

Mocking

  • Mocking memberikan tambahan pemeriksaan apakah emailnya sudah terkirim

verify(emailSender).sendEmail(sampleCustomer);

  • atau email nya tidak terkirim (times(0) menyatakan belum ada pemanggilan)

verify(emailSender, times(0)).sendEmail(sampleCustomer);

38

39 of 41

FIRST

  • Fast: proses testing secara keseluruhan harus dilakukan dengan cepat.
  • Isolated: setiap unit test tidak boleh memengaruhi hasil unit test lain.
  • Repeatable: hasil tes konsisten meskipun dijalankan berkali-kali pada kondisi yang sama.
  • Self-Validating: bisa digunakan untuk mengecek kesesuaian aplikasi jika terjadi perubahan kode.
  • Thorough: sebisa mungkin mencakup keseluruhan kode dan business logic aplikasi.

39

40 of 41

Q

A

&

40

41 of 41

Reading and Watching

41