Topik ini membangun alat konversi satuan panjang dari nol dengan siklus kerja bertitik pemeriksaan. Setiap potongan kecil segera diperiksa dengan dart analyze, diuji dengan dart test, dan dirapikan dengan dart format sebelum potongan berikutnya ditulis. Satu test dengan ekspektasi yang keliru sengaja dijalankan agar kegagalan yang dilaporkan oleh alat dapat diamati, kemudian diperbaiki sampai seluruh test lulus. Seluruh langkah dieksekusi pada proyek mini bernama konversi_satuan, dan setiap perintahnya ditampilkan beserta keluaran aslinya. Hasil akhirnya adalah alat baris perintah yang bersih pada ketiga alat pemeriksa dan siap dijalankan.
Kerangka proyek dan titik pemeriksaan pertama#
Proyek Dart menyusun kodenya dalam struktur yang telah disepakati alat-alatnya. Berkas pubspec.yaml pada akar proyek mendeklarasikan nama, deskripsi, batasan versi SDK, dan dependensi. Kode yang diimpor oleh bagian lain diletakkan pada folder lib, test diletakkan pada folder test, dan program yang dieksekusi langsung diletakkan pada folder bin. Alat konversi satuan yang dibangun pada topik ini mengikuti struktur tersebut sejak baris pertamanya.
Berkas pubspec.yaml proyek ini memuat satu dependensi pengembangan, yaitu package test yang menyediakan fungsi test dan expect untuk pengujian. Batasan test: ^1.33.0 mengizinkan versi 1.33.0 ke atas sampai sebelum 2.0.0, dan pub memilih versi terbaru yang cocok saat resolusi berlangsung. Seluruh berkas proyek ditulis melalui heredoc, sehingga setiap blok dapat diulang dari keadaan kosong tanpa menyisakan berkas lama.
name: konversi_satuan
description: Alat konversi satuan panjang untuk latihan titik pemeriksaan.
version: 1.0.0
environment:
sdk: ^3.13.0
dev_dependencies:
test: ^1.33.0Titik pemeriksaan pertama menuliskan pubspec.yaml beserta enum Satuan sebagai fondasi modelnya, lalu menutupnya dengan dart pub get dan dart analyze. Enum Satuan membawa faktor konversi setiap satuan ke meter, sehingga perhitungan pada langkah berikutnya cukup berpindah dari satu faktor ke faktor yang lain. Dengan faktor tersimpan pada enum, tabel konversi berpasangan antarsatuan tidak diperlukan sama sekali.
# buat kerangka proyek konversi_satuan dari nol
rm -rf ~/contoh/pengerjaan-proyek/membangun-dengan-titik-pemeriksaan/konversi_satuan
mkdir -p ~/contoh/pengerjaan-proyek/membangun-dengan-titik-pemeriksaan/konversi_satuan/lib
cd ~/contoh/pengerjaan-proyek/membangun-dengan-titik-pemeriksaan/konversi_satuan
export PATH=/home/kal/dart-sdk/bin:$PATH
cat > pubspec.yaml <<'EOF'
name: konversi_satuan
description: Alat konversi satuan panjang untuk latihan titik pemeriksaan.
version: 1.0.0
environment:
sdk: ^3.13.0
dev_dependencies:
test: ^1.33.0
EOF
cat > lib/konversi.dart <<'EOF'
enum Satuan {
meter(1.0),
kilometer(1000.0),
kaki(0.3048),
mil(1609.344);
const Satuan(this.faktorKeMeter);
final double faktorKeMeter;
}
EOF
dart pub get
dart analyzeResolving dependencies...
Downloading packages...
+ _fe_analyzer_shared 111.0.0
+ analyzer 14.7.0
+ args 2.7.0
+ async 2.13.1
+ boolean_selector 2.1.2
+ cli_config 0.2.0
+ cli_util 0.6.0
+ collection 1.19.1
+ convert 3.1.2
+ coverage 1.16.0
+ crypto 3.0.8
+ ffi 2.2.0
+ file 7.0.1
+ frontend_server_client 4.2.0
+ glob 2.2.0
+ http_multi_server 3.2.3
+ http_parser 4.1.2
+ io 1.1.0
+ logging 1.3.0
+ matcher 0.12.20
+ meta 1.19.0
+ mime 2.1.0
+ node_preamble 2.0.2
+ package_config 3.0.0
+ path 1.9.1
+ pool 1.5.3
+ pub_semver 2.2.1
+ shelf 1.4.3
+ shelf_packages_handler 3.0.2
+ shelf_static 1.2.0
+ shelf_web_socket 3.0.0
+ source_map_stack_trace 2.1.2
+ source_maps 0.10.14
+ source_span 1.10.2
+ stack_trace 1.12.2
+ stream_channel 2.1.4
+ string_scanner 1.4.1
+ term_glyph 1.2.2
+ test 1.33.0
+ test_api 0.7.15
+ test_core 0.6.21
+ typed_data 1.4.0
+ vm_service 15.4.0
+ watcher 1.2.1
+ web 1.1.2
+ web_socket 1.0.1
+ web_socket_channel 3.0.3
+ webkit_inspection_protocol 1.2.1
+ yaml 3.1.4
Changed 49 dependencies!
Analyzing konversi_satuan...
No issues found!Keluaran pertama berasal dari dart pub get, yang menyelesaikan dependensi dan mengunduh package test beserta seluruh dependensi turunannya. Baris penutupnya, Changed 49 dependencies!, melaporkan jumlah package yang baru masuk ke proyek. Titik pemeriksaan pertama ditutup oleh dart analyze dengan keluaran No issues found!, yang berarti kerangka proyek bersih sebelum satu pun perilaku ditulis. Kerangka yang bersih di awal membuat sumber setiap temuan berikutnya selalu berupa potongan yang baru ditambahkan.
Implementasi dengan ekspektasi yang keliru#
Titik pemeriksaan kedua menambahkan perilaku pertama, yaitu fungsi konversi di samping enum Satuan. Fungsi konversi mengalikan nilai dengan faktor satuan asal sehingga menjadi meter, lalu membaginya dengan faktor satuan tujuan. Bentuk dua langkah ini menghindari tabel konversi berpasangan antarsatuan, karena setiap satuan cukup membawa faktornya sendiri ke satu titik tengah. Berkas lib/konversi.dart setelah implementasinya berikut ini.
enum Satuan {
meter(1.0),
kilometer(1000.0),
kaki(0.3048),
mil(1609.344);
const Satuan(this.faktorKeMeter);
final double faktorKeMeter;
}
double konversi(double nilai, Satuan dari, Satuan ke) {
final dalamMeter = nilai * dari.faktorKeMeter;
return dalamMeter / ke.faktorKeMeter;
}Test pertama ditulis bersama implementasinya dengan satu ekspektasi yang belum diverifikasi, yaitu lima kilometer dianggap sama dengan 3,2 mil. Angka 3.2 pada ekspektasinya berasal dari perkiraan kasar yang belum dihitung ulang, sedangkan matcher closeTo dipilih karena hasil konversinya desimal panjang dan syaratnya cukup tepat sampai tiga desimal. Keadaan semacam ini lazim pada pengerjaan nyata, yaitu ekspektasi ditulis lebih dahulu daripada perhitungannya diperiksa. Blok berikut menuliskan implementasi dan test tersebut, lalu menjalankan dart analyze dan dart test secara berurutan.
# tulis fungsi konversi dan test pertama dengan ekspektasi yang keliru
cd ~/contoh/pengerjaan-proyek/membangun-dengan-titik-pemeriksaan/konversi_satuan
export PATH=/home/kal/dart-sdk/bin:$PATH
cat > lib/konversi.dart <<'EOF'
enum Satuan {
meter(1.0),
kilometer(1000.0),
kaki(0.3048),
mil(1609.344);
const Satuan(this.faktorKeMeter);
final double faktorKeMeter;
}
double konversi(double nilai, Satuan dari, Satuan ke) {
final dalamMeter = nilai * dari.faktorKeMeter;
return dalamMeter / ke.faktorKeMeter;
}
EOF
mkdir -p test
cat > test/konversi_test.dart <<'EOF'
import 'package:konversi_satuan/konversi.dart';
import 'package:test/test.dart';
void main() {
test('lima kilometer sama dengan 3,2 mil', () {
expect(konversi(5, Satuan.kilometer, Satuan.mil), closeTo(3.2, 0.001));
});
}
EOF
dart analyze
dart testAnalyzing konversi_satuan...
No issues found!
00:00 +0: loading test/konversi_test.dart
00:00 +0: test/konversi_test.dart: lima kilometer sama dengan 3,2 mil
00:00 +0 -1: test/konversi_test.dart: lima kilometer sama dengan 3,2 mil [E]
Expected: a numeric value within <0.001> of <3.2>
Actual: <3.1068559611866697>
Which: differs by <0.09314403881333044>
package:matcher expect
test/konversi_test.dart 6:5 main.<fn>
00:00 +0 -1: Some tests failed.
Failing tests:
test/konversi_test.dart: lima kilometer sama dengan 3,2 mil
Consider enabling the flag chain-stack-traces to receive more detailed exceptions.
For example, 'dart test --chain-stack-traces'.Keluaran ini memperlihatkan perbedaan penting antara kedua alat pemeriksa. Pemeriksaan dart analyze melaporkan No issues found! karena ekspektasi 3.2 sah menurut aturan bahasa, sedangkan dart test menolaknya saat pernyataan dieksekusi. Baris Expected dan Actual menampilkan kedua nilai yang berselisih, baris Which menyebut selisih 0,09314403881333044, dan angka itu jauh melampaui batas 0,001 yang ditetapkan matcher. Dua baris di bawahnya menunjuk lokasi pemanggilan expect, yaitu baris 6 pada berkas test. Keberadaan dua alat dengan cakupan berbeda inilah alasan keduanya dijalankan berurutan pada setiap titik pemeriksaan, karena analyzer tidak menilai kebenaran hasil hitung dan test tidak menilai kebersihan sintaks.
Perbaikan ekspektasi sampai test lulus#
Keluaran yang gagal tidak memerlukan penalaran tambahan karena nilai sebenarnya sudah tertulis di dalamnya, yaitu 3,1068559611866697. Perbaikan yang tepat menuliskan ekspektasi baru mengikuti nilai tersebut, yaitu 3,107 sebagai pembulatan tiga desimal, dan bukan memperlebar batas matcher agar test lulus dengan sendirinya. Ekspektasi yang diperlebar hanya memindahkan kesalahan ke tempat lain, sedangkan ekspektasi yang diperbaiki membuat test kembali berarti. Blok berikut menuliskan versi yang benar test tersebut melalui heredoc yang menulis ulang seluruh berkasnya, lalu menjalankan ulang kedua pemeriksaan.
# tulis ulang test dengan ekspektasi yang benar, lalu periksa ulang
cd ~/contoh/pengerjaan-proyek/membangun-dengan-titik-pemeriksaan/konversi_satuan
export PATH=/home/kal/dart-sdk/bin:$PATH
cat > test/konversi_test.dart <<'EOF'
import 'package:konversi_satuan/konversi.dart';
import 'package:test/test.dart';
void main() {
test('lima kilometer sama dengan 3,107 mil', () {
expect(konversi(5, Satuan.kilometer, Satuan.mil), closeTo(3.107, 0.001));
});
}
EOF
dart analyze
dart testAnalyzing konversi_satuan...
No issues found!
00:00 +0: loading test/konversi_test.dart
00:00 +0: test/konversi_test.dart: lima kilometer sama dengan 3,107 mil
00:00 +1: All tests passed!Kedua alat kini melaporkan keadaan bersih, yaitu No issues found! dan All tests passed!. Angka +1 pada baris penutup menghitung satu test yang lulus, dan nilainya bertambah mengikuti setiap test baru yang diterapkan. Siklus merah ke hijau yang ditempuh dua blok terakhir adalah pola kerja inti topik ini, yaitu potongan kecil ditulis, diperiksa, diperbaiki, lalu dilanjutkan dengan potongan berikutnya. Kesalahan ekspektasi semacam ini tertangkap dalam hitungan detik, jauh sebelum kode yang salah berpindah ke tempat lain.
Format sebagai titik pemeriksaan penutup#
Titik pemeriksaan keempat menyangkut gaya penulisan, dan dart format menjalankannya dengan satu perintah. Alat ini memiliki gaya yang sengaja dibuat tetap, sehingga tidak ada pengaturan format yang dikelola per proyek dan perdebatan tentang gaya berakhir pada satu perintah. Test kedua ditambahkan untuk menegaskan bahwa konversi bolak-balik dari satu satuan ke satuan lain menghasilkan kembali nilai semula. Versi tulisan pertamanya sengaja menggunakan indentasi yang tertukar dan baris expect yang terlalu panjang, sedangkan berkas kanonis setelah diformat berikut ini.
import 'package:konversi_satuan/konversi.dart';
import 'package:test/test.dart';
void main() {
test('lima kilometer sama dengan 3,107 mil', () {
expect(konversi(5, Satuan.kilometer, Satuan.mil), closeTo(3.107, 0.001));
});
test('konversi bolak-balik kembali ke nilai awal', () {
final nilai = 12.5;
expect(
konversi(
konversi(nilai, Satuan.mil, Satuan.kaki),
Satuan.kaki,
Satuan.mil,
),
closeTo(nilai, 0.0000001),
);
});
}Blok berikut menuliskan versi yang belum rapi, merapikannya dengan dart format, menampilkan isi berkas setelah format, lalu menjalankan dart analyze dan dart test untuk memastikan perapian tidak mengubah perilakunya. Keluaran ringkas dart format diarahkan ke /dev/null karena perubahan yang dibuatnya cukup dibuktikan melalui isi berkasnya. Gaya tulisan yang berubah hanya menyangkut penataan, dan bukan urutan pernyataan maupun nilai yang diperiksa.
# tulis ulang test dengan test kedua dalam gaya yang belum rapi
cd ~/contoh/pengerjaan-proyek/membangun-dengan-titik-pemeriksaan/konversi_satuan
export PATH=/home/kal/dart-sdk/bin:$PATH
cat > test/konversi_test.dart <<'EOF'
import 'package:konversi_satuan/konversi.dart';
import 'package:test/test.dart';
void main() {
test('lima kilometer sama dengan 3,107 mil', () {
expect(konversi(5, Satuan.kilometer, Satuan.mil), closeTo(3.107, 0.001));
});
test('konversi bolak-balik kembali ke nilai awal', () {
final nilai = 12.5;
expect(konversi(konversi(nilai, Satuan.mil, Satuan.kaki), Satuan.kaki, Satuan.mil), closeTo(nilai, 0.0000001));
});
}
EOF
# rapikan dengan dart format, lalu periksa isinya
dart format test/konversi_test.dart > /dev/null
cat test/konversi_test.dart
# jalankan dua pemeriksaan setelah format
dart analyze
dart testimport 'package:konversi_satuan/konversi.dart';
import 'package:test/test.dart';
void main() {
test('lima kilometer sama dengan 3,107 mil', () {
expect(konversi(5, Satuan.kilometer, Satuan.mil), closeTo(3.107, 0.001));
});
test('konversi bolak-balik kembali ke nilai awal', () {
final nilai = 12.5;
expect(
konversi(
konversi(nilai, Satuan.mil, Satuan.kaki),
Satuan.kaki,
Satuan.mil,
),
closeTo(nilai, 0.0000001),
);
});
}
Analyzing konversi_satuan...
No issues found!
00:00 +0: loading test/konversi_test.dart
00:00 +0: test/konversi_test.dart: lima kilometer sama dengan 3,107 mil
00:00 +1: test/konversi_test.dart: konversi bolak-balik kembali ke nilai awal
00:00 +2: All tests passed!Keluaran menampilkan isi berkas setelah dart format menulis ulang gayanya, yaitu indentasi empat spasi yang konsisten dan pemanggilan expect yang dibungkus agar setiap argumennya terbaca. Perubahan itu hanya menyentuh penataan, dan buktinya adalah dua pemeriksaan di bawahnya yang tetap bersih, dengan dua test yang lulus tanpa penyesuaian apa pun. Batas 0,0000001 pada test kedua dipilih karena dua pembagian dengan faktor berbeda dapat menyisakan selisih pembulatan yang sangat kecil. Siklus tulis, periksa, uji, dan rapikan kini lengkap, sehingga langkah berikutnya cukup mengulang pola yang sama untuk setiap fitur baru.
Menjalankan alat hasil pengerjaan#
Proyek yang bersih pada ketiga alat hanya bermakna ketika alatnya benar-benar dijalankan. Antarmuka baris perintah diletakkan pada bin/konversi_satuan.dart sesuai bentuk yang dipilih untuk proyek ini, yaitu program kecil yang melayani satu orang di terminal. Daftar tugas konversinya ditulis sebagai record dan dibongkar langsung pada baris perulangannya, sehingga pasangan nilai dan satuan tetap berdampingan tanpa kelas tambahan. Berkas tersebut berikut ini.
import 'package:konversi_satuan/konversi.dart';
void main() {
final tugas = [
(5.0, Satuan.kilometer, Satuan.mil),
(100.0, Satuan.meter, Satuan.kaki),
];
for (final (nilai, dari, ke) in tugas) {
final hasil = konversi(nilai, dari, ke);
print(
'${nilai.toStringAsFixed(1)} ${dari.name} = ${hasil.toStringAsFixed(3)} ${ke.name}',
);
}
}Blok berikut menuliskan antarmuka itu, memeriksa dengan dart analyze, lalu mengeksekusinya dengan dart run. Titik pemeriksaan terakhir ini menutup pengerjaan dengan bukti penggunaan, bukan sekadar test yang lulus. Kebenaran keluarannya dinilai terhadap ekspektasi yang telah disepakati oleh test sebelumnya.
# tambahkan antarmuka baris perintah, lalu jalankan alatnya
cd ~/contoh/pengerjaan-proyek/membangun-dengan-titik-pemeriksaan/konversi_satuan
export PATH=/home/kal/dart-sdk/bin:$PATH
mkdir -p bin
cat > bin/konversi_satuan.dart <<'EOF'
import 'package:konversi_satuan/konversi.dart';
void main() {
final tugas = [
(5.0, Satuan.kilometer, Satuan.mil),
(100.0, Satuan.meter, Satuan.kaki),
];
for (final (nilai, dari, ke) in tugas) {
final hasil = konversi(nilai, dari, ke);
print(
'${nilai.toStringAsFixed(1)} ${dari.name} = ${hasil.toStringAsFixed(3)} ${ke.name}',
);
}
}
EOF
dart analyze
dart run bin/konversi_satuan.dartAnalyzing konversi_satuan...
No issues found!
5.0 kilometer = 3.107 mil
100.0 meter = 328.084 kakiKeluaran menampilkan dua konversi yang dihitung oleh alat, yaitu 5 kilometer sama dengan 3,107 mil dan 100 meter sama dengan 328,084 kaki. Nilai 3,107 pada keluaran pertama cocok dengan ekspektasi yang sebelumnya memerlukan satu perbaikan, sehingga benang merah antara test, implementasi, dan penggunaan terlihat utuh. Proyek kecil ini lahir dari lima titik pemeriksaan yang masing-masing menyisakan keadaan bersih. Pola yang sama dapat diteruskan untuk fitur berikutnya, misalnya penambahan satuan suhu atau pembacaan argumen baris perintah.
Ringkasan#
- Titik pemeriksaan pada pengerjaan berupa dart analyze, dart test, dan dart format yang dijalankan berurutan setelah setiap potongan kode ditulis.
- dart analyze yang bersih tidak menjamin test lulus, karena ekspektasi yang keliru baru tertangkap saat pernyataan dieksekusi dan dilaporkan melalui baris Expected dan Actual.
- Perbaikan ekspektasi mengikuti nilai yang dilaporkan test, bukan pelebaran batas matcher, sehingga test yang lulus tetap berarti.
- dart format menulis gaya kanonis tanpa pengaturan, dan dart analyze serta dart test setelahnya menegaskan bahwa perapian tidak mengubah perilaku.
Referensi#
- Dart testing — dart.dev — Panduan resmi penulisan dan pengeksekusian test pada proyek Dart.
- dart format — dart.dev — Dokumentasi dart format beserta opsi baris perintah dan gaya formatnya.
- Customizing static analysis — dart.dev — Panduan mengatur analyzer dan aturan linter pada proyek Dart.
- dart tool — dart.dev — Referensi perintah dart, termasuk analyze, test, dan format beserta opsinya.