Jest: Konteks yang dapat dibaca manusia untuk ekspektasi

Dibuat pada 21 Okt 2016  ·  76Komentar  ·  Sumber: facebook/jest

Jika ada beberapa harapan dalam satu it , saat ini tampaknya mustahil untuk mengetahui harapan mana yang benar-benar gagal tanpa referensi silang kegagalan dengan nomor baris dalam kode Anda.

test('api works', () => {
    expect(api()).toEqual([]) // api without magic provides no items
    expect(api(0)).toEqual([]) // api with zero magic also provides no items
    expect(api(true)).toEqual([1,2,3]) // api with magic enabled provides all items
})

Harapan mana yang gagal? Yang pertama atau kedua?

image

Akan lebih baik jika ada beberapa konteks yang dapat dibaca manusia yang segera memperjelas harapan mana yang gagal dan apa arti sebenarnya dari keluaran harapan dalam istilah manusia, tanpa harus menemukan nomor baris di bagian atas jejak tumpukan dan memetakannya kembali ke kode.


Bandingkan tape yang setara di bawah ini. Abaikan bahwa pita tidak menjamin setelah kegagalan pernyataan pertama. tape mencetak pesan yang dapat dibaca manusia di atas setiap kegagalan ekspektasi, memungkinkan Anda mengetahui dengan tepat pengujian mana yang gagal tanpa kembali ke file pengujian.

Perhatikan ini juga mendorong kebisingan yang dapat dibaca manusia ke akhir baris di sumber pengujian, tempat Anda tetap dapat menulis komentar.

test('api works', t => {
  t.deepEquals(api(), [], 'api without magic provides no items')
  t.deepEquals(api(0), [], 'api with zero magic also provides no items')
  t.deepEquals(api(true), [1,2,3], 'api with magic enabled provides all items')
})

image


Tampaknya satu-satunya cara untuk melampirkan informasi yang dapat dibaca manusia ke kesalahan dengan jest adalah dengan membungkus semuanya dengan it tambahan yang merupakan IMO verbose yang tidak perlu.

describe('api works', () => {
  test('api without magic provides no items', () => {
    expect(api()).toEqual([])
  })
  test('api with zero magic also provides no items', () => {
    expect(api(0)).toEqual([])
  })
  test('api with magic enabled provides all items', () => {
    expect(api(true)).toEqual([1,2,3])
  })
})

Idealnya, seseorang dapat melampirkan beberapa konteks yang dapat dibaca manusia di akhir expect .

misalnya

Pesan konteks sebagai parameter opsional tambahan untuk metode pernyataan:

test('api works', () => {
    expect(api()).toEqual([], 'api without magic provides no items')
    expect(api(0)).toEqual([], 'api with zero magic provides no items')
    expect(api(true)).toEqual([1,2,3], 'api with magic enabled provides all items')
})


Atau pesan konteks sebagai .because atau .why atau .comment atau .t atau apa pun:

test('api works', () => {
    expect(api()).toEqual([]).because('api without magic provides no items')
    expect(api(0)).toEqual([]).because('api with zero magic provides no items')
    expect(api(true)).toEqual([1,2,3]).because('api with magic enabled provides all items')
})

Atau, mungkin akan lebih baik jika lelucon bisa dengan mudah membaca file dan mencetak baris kode sumber aktual yang sesuai dengan harapan itu sendiri.

Komentar yang paling membantu

Dari diskusi ini dan repositori ini saya pikir yang bagus dan semantik adalah:

it('has all the methods', () => {
  since('cookie is a method').expect(reply.cookie).toBeDefined();
  since('download is a method').expect(reply.download).toBeDefined();
  since('end is a method').expect(reply.end).toBeDefined();
  // ...
});

Penggunaannya mirip dengan because , tetapi secara semantik lebih masuk akal.

Jika Anda menyukai ini, saya mungkin dapat mengerjakan PR dengan menambahkan fungsionalitas since .

Semua 76 komentar

Hai! Jadi kami sebenarnya pernah memiliki ini di Jasmine tetapi menemukan bahwa lebih dari ribuan file uji di FB, tidak ada yang menggunakannya. Jadi untuk saat ini kami sedang mencetak pesan kesalahan yang bagus dengan informasi perkiraan dan jejak tumpukan yang akan mengarah ke harapan (seperti di tangkapan layar Anda). Saya setuju kita bisa mencetak garis yang melempar tetapi cukup sering pernyataannya panjangnya beberapa baris:

expect(a).toEqual({
  …
});

jadi ini tidak akan benar-benar terlihat bagus dan kami harus menggunakan parser untuk mengurai JS dan mengekstrak info yang relevan (dan menciutkan garis panjang) atau sesuatu yang serupa untuk membuatnya cantik.

Secara pribadi saya pikir kami menunjukkan informasi yang cukup untuk saat ini tetapi senang untuk mempertimbangkan kembali. Jika Anda memiliki ide untuk sesuatu yang tidak terlalu rumit tetapi menambahkan lebih banyak konteks yang membantu menyelesaikan masalah lebih cepat, beri tahu saya.

kami sebenarnya pernah memiliki ini di Jasmine tetapi menemukan bahwa lebih dari ribuan file uji di FB, tidak ada yang menggunakannya

@cpojer jadi polanya adalah membungkus setiap pernyataan dalam it ? dan/atau hanya percaya pada nomor baris?

Mungkinkah pola ini telah diadopsi lebih sedikit karena lebih baik atau lebih buruk, tetapi lebih hanya untuk konsistensi dengan tes yang ada? atau mungkin tidak mengetahui fitur tersebut ada? Saya tidak tahu ini ada di Jasmine.

Saya setuju kita bisa mencetak garis yang melempar tetapi cukup sering pernyataannya panjangnya banyak baris

Memfaktorkan ulang ke satu baris dapat mendorong lebih banyak informasi semantik dalam pernyataan? Mungkin?

const adminUser = {
  …
}
expect(a).toEqual(adminUser);

Secara pribadi saya pikir kami menunjukkan informasi yang cukup untuk saat ini tetapi senang untuk mempertimbangkan kembali

Contoh di atas menunjukkan bahwa sulit untuk menemukan dengan tepat pernyataan mana yang gagal kecuali Anda menambahkan pembungkus verbose (IMO) di sekitar semuanya. Hal ini terutama benar dalam lingkungan transpiled di mana nomor baris peta sumber tidak selalu akurat. Saya percaya bahwa pemahaman yang cepat dan akurat pecah dan di mana penting, seperti juga tes ringkas.

Jika Anda memiliki ide untuk sesuatu yang tidak terlalu rumit tetapi menambahkan lebih banyak konteks yang membantu menyelesaikan masalah lebih cepat, beri tahu saya.

Saya membuat beberapa saran di atas:

Apakah Anda mencari sesuatu yang lebih sederhana, atau berbeda?

hai @timoxley! kami sudah berpikir untuk menambahkan sesuatu seperti ini.

jadi masalah dengan opsi pertama adalah bahwa beberapa pencocokan memiliki argumen opsional, dan itu membuat segalanya menjadi lebih rumit.

misalnya di sini pada kasus kedua kita tidak akan tahu apakah argumennya adalah kedekatan atau pesan kesalahan

expect(555).toBeCloseTo(111, 2, 'reason why');
expect(555).toBeCloseTo(111, 'reason why');

saran kedua tidak akan berhasil karena korek api akan melempar segera setelah sesuatu tidak memenuhi harapan

expect(1).toBe(2)/* will throw here */.because('reason');

kita bisa melampirkan alasan sebelum matcher dijalankan, seperti ini:

expect(1).because('reason').toBe(2);
// or 
because('reason').expect(1).toBe(2);

tetapi API ini tidak terlalu terlihat bagus.

opsi lain adalah menambahkan argumen kedua ke expect

expect(1, 'just because').toBe(2);

tapi itu hampir sama dengan opsi sebelumnya.

Alasan mengapa saya pikir ini tidak terlalu berguna adalah karena para insinyur tidak ingin membuang waktu untuk menulis tes. Apa pun yang kita lakukan untuk mempersulit mereka hanya akan mengarah pada ujian yang lebih buruk.

Di masa lalu, solusi terbaik sebenarnya adalah membuat pencocokan khusus. Kami akan memperkenalkan expect.extend di versi Jest berikutnya dan ini akan memungkinkan Anda membuat pencocokan dengan mudah seperti:

expect(a).toEqualMySpecificThing(…)

yang seharusnya memungkinkan Anda untuk menulis pesan kegagalan yang lebih ekspresif. Saya telah melihat ini banyak digunakan dalam proyek-proyek seperti Relay. Lihat semua pencocokan: https://github.com/facebook/relay/blob/master/src/tools/__mocks__/RelayTestUtils.js#L281

Tutup karena tidak aktif tetapi senang untuk membuka kembali jika ada ide bagus.

@cpojer @dmitriiabramov mohon maaf atas keterlambatannya.

Argumen kedua ke expect atau merangkai alasan dengan .because akan bagus. Apa yang perlu dilakukan untuk mewujudkannya atau tidak?

insinyur tidak ingin membuang waktu menulis tes

@cpojer Setuju! Selain tidak ingin membuang waktu untuk menguji debug, inilah tepatnya mengapa saya percaya API yang lebih sedikit bertele-tele dengan lebih banyak konteks kegagalan akan lebih disukai.

Untuk beberapa angka konkret, menggunakan contoh sederhana dari komentar saya di atas, untuk mendapatkan pernyataan + konteks yang setara dengan pita, Jest mengharuskan programmer menulis hampir dua kali lipat jumlah boilerplate seremonial:

  • 1,8x garis (6 vs 11)
  • 2x lekukan (1 vs 2)
  • 2x parens/curly (24 vs 48) !

Ini bisa ditingkatkan!

// tape
test('api works', t => {
  t.deepEquals(api(), [], 'api without magic provides no items')
  t.deepEquals(api(0), [], 'api with zero magic also provides no items')
  t.deepEquals(api(true), [1,2,3], 'api with magic enabled provides all items')
  t.end()
})

// jest
describe('api works', () => {
  test('api without magic provides no items', () => {
    expect(api()).toEqual([])
  })
  test('api with zero magic also provides no items', () => {
    expect(api(0)).toEqual([])
  })
  test('api with magic enabled provides all items', () => {
    expect(api(true)).toEqual([1,2,3])
  })
})

Pembaruan : Saya kira Anda bisa menulis tes lelucon pada satu baris dengan panah:

// jest
describe('api works', () => {
  test('api without magic provides no items', () => expect(api()).toEqual([]))
  test('api with zero magic also provides no items', () => expect(api(0)).toEqual([]))
  test('api with magic enabled provides all items', () => expect(api(true)).toEqual([1,2,3]))
})

Ini membuat beberapa baris lebih panjang, tetapi meningkatkan statistik yang kami bandingkan sebelumnya:

  • 0,8x garis (6 vs 5)
  • 1x lekukan (1 vs 1)
  • 1,75x parens/curly (24 vs 42)

Namun saya pikir memiliki deskripsi tes di awal baris, tanpa linebreak, membuat lebih sulit untuk menguraikan logika secara visual karena menempatkan "daging" tes, yaitu pernyataan aktual, di beberapa posisi kolom arbitrer.

Mengurai kode lebih penting daripada membaca deskripsi pengujian, yang pada dasarnya hanya komentar yang dimuliakan. Inilah sebabnya mengapa tidak ada yang menulis komentar di awal baris .misalnya ini akan menjadi kegilaan sadomasokis:

/* api without magic provides no items */ expect(api()).toEqual([])
/* api with zero magic also provides no items */ expect(api(0)).toEqual([])
/* api with magic enabled provides all items */ expect(api(true)).toEqual([1,2,3])

Idealnya semua kode pernyataan akan berbaris rapi di kolom yang sama sehingga mudah diurai oleh manusia. Berdasarkan pemikiran ini, saya akan sangat memilih bentuk .because tambahan daripada saran alternatif dari argumen kedua ke expect .

Terima kasih untuk menjaga percakapan tetap berjalan. Perhatikan, bahwa Anda juga tidak memerlukan blok deskripsi, yang selanjutnya membuat segalanya lebih kecil. .because sayangnya tidak akan berfungsi karena ketika matcher melempar (yang terjadi sebelum .because dipanggil), kami tidak akan memiliki cara untuk mengekstrak nama tersebut.

Gunakan argumen terakhir dari setiap fungsi pencocokan?

Ini hanya akan berfungsi jika kita menambahkannya sebagai argumen kedua dari expect .
Kami tidak dapat menambahkannya sebagai argumen terakhir untuk setiap fungsi pencocokan karena ambiguitas
misalnya

expect(obj).toHaveProperty('a.b.c', 'is that a reason or a value of the property?');

Tutup karena tidak aktif tetapi senang untuk membuka kembali jika ada ide bagus.

@cpojer tidak jelas apakah cara melati (dan lainnya) expect(value).toBe(something, 'because message') telah dikesampingkan?

Salah satu contohnya adalah menguji hal-hal redux-saga/redux-observable di mana Anda menguji state machine . Ini sangat membantu untuk memiliki pesan deskriptif tentang keadaan gagal apa. Contoh itu dibuat-buat jadi deskripsinya juga..

@jayphelps kami tidak menggunakan cara melati lagi karena kami menulis ulang semua pencocokan melati

@dmitriiabramov maaf pertanyaan saya tidak jelas. Apakah _cara_ melati melakukannya telah diatur untuk ditambahkan kembali? Melakukan hal yang sama yang mereka izinkan.

@jayphelps seperti yang saya katakan sebelumnya, itu tidak akan berfungsi untuk semua pencocokan karena ambiguitas.

expect(obj).toHaveProperty('a.b.c', 'is that a reason or a value of the property?');

dan sing jest matcher dapat diperpanjang dengan paket pihak ketiga, saya rasa bukan ide yang baik untuk mengacaukan daftar argumen

opsi terbersih mungkin memilikinya sebagai argumen kedua dari expect , karena selalu membutuhkan tepat satu argumen.

expect(123, 'jest because').toEqual(123);

saya tidak yakin apakah kita ingin membebani API secara berlebihan. Kami hampir tidak pernah menggunakannya di suite pengujian facebook, dan untuk kasus khusus saya pikir lebih mudah untuk mendefinisikan tes baru:

beforeEach(someSharedSetup);
test('reason or description', () => expect(1).toBe(1));

tinggal beberapa baris lagi :)

Atau Anda bahkan dapat memasukkannya ke dalam panggilan describe() lainnya.

@dmitriiabramov Kasus yang mengganggu adalah ketika Anda membangun status, seperti di mesin status untuk saga, epos, dll. Setiap tes memerlukan perubahan status sebelumnya, mengisolasinya membutuhkan banyak duplikasi tanpa mendapatkan AFAIK.

it('stuff', () => {
  const generator = incrementAsync();

  expect(generator.next().value).toBe(
    call(delay, 1000)
  );

  expect(generator.next().value).toBe(
    put({ type: 'INCREMENT' })
  );

  expect(generator.next()).toBe(
    { done: true, value: undefined }
  );
});

Atau Anda bahkan dapat memasukkannya ke dalam panggilan deskripsi() lain.

Bisakah Anda menguraikan ini? Bersarang menggambarkan panggilan AFAIK hanya untuk membagi judul bagian, tes masih berjalan secara bersamaan kan?

Test suite (file) berjalan secara bersamaan, panggilan test() tidak.

saya mulai berpikir bahwa sesuatu seperti

test('111' () => {
  jest.debug('write something only if it fails');
  expect(1).toBe(2);
});

bisa menjadi sesuatu

Dari diskusi ini dan repositori ini saya pikir yang bagus dan semantik adalah:

it('has all the methods', () => {
  since('cookie is a method').expect(reply.cookie).toBeDefined();
  since('download is a method').expect(reply.download).toBeDefined();
  since('end is a method').expect(reply.end).toBeDefined();
  // ...
});

Penggunaannya mirip dengan because , tetapi secara semantik lebih masuk akal.

Jika Anda menyukai ini, saya mungkin dapat mengerjakan PR dengan menambahkan fungsionalitas since .

Silakan terapkan cara mudah untuk melakukannya. Saya tidak sering menggunakannya, tetapi terutama untuk tes yang lebih rumit, akan sangat membantu untuk mengetahui dengan tepat apa yang gagal tanpa harus menggali.

Tolong jangan katakan "tulis ulang test suite Anda menjadi lebih sederhana". Satu-satunya hal yang lebih dibenci para insinyur daripada _writing_ test suites adalah _rewriting_ test suites.

Proposal lain yang pernah saya lihat di suatu tempat, saya lupa di mana Itu dalam masalah yang sama, dengan penjelasan mengapa itu tidak berhasil. Mungkin aku harus tidur :)

Saya mendapatkan beberapa demo "prototipe" sederhana yang berfungsi, saya perlu mengimplementasikan rekursi sekarang. Ini adalah pembungkus tipis menggunakan Proksi di sekitar variabel global dan kemudian di atas setiap metode. Namun, Proxy tidak didukung oleh browser lama dan tidak dapat diisi poli sehingga mungkin tidak dapat diterima untuk Jest. Ini adalah struktur umum untuk pembungkusnya:

const since = (text) => {
  return new Proxy(global, {
    get: (orig, key) => {
      return (...args) => {
        try {
          const stack = orig[key](...args);
          return new Proxy(stack, {
            get: (orig, key) => {
              return (...args) => {
                try {
                  const ret = orig[key](...args);

                  // ... implement recursion here

                } catch (err) {
                  console.log('2', key, text, err);
                  throw err;
                }
              }
            }
          });
        } catch (err) {
          console.log('1', key, text, err);
          throw err;
        }
      };
    }
  });
};

Ada tiga opsi realistis:

  • Cara ini dapat diterima sehingga harus ditambahkan ke perpustakaan Jest utama. Saya membersihkannya dan membuat PR.
  • Gali lebih dalam Jest dan modifikasi pustaka inti. Banyak pekerjaan, jadi tidak akan melakukan apa pun sampai beberapa anggota mengatakan sesuatu secara semi-resmi tentang masalah/arah ini.
  • Selesaikan dengan cara ini dan publikasikan sebagai paket. Tidak diinginkan karena tidak mudah ditemukan.

Sunting: lihat beraksi:

describe('Test', () => {
  it('works', () => {
    since('It fails!').expect('a').toEqual('b');
  });
});

Anda memerlukan konteks ekspektasi untuk membuat hasil tes menjadi waras ketika Anda memiliki tes non-sepele. Tes dunia nyata tidak akan selalu sesederhana itu.

Ingat pencocokan khusus - mereka menyembunyikan kerumitan matematika. Tetapi ketika pengujian gagal, menyembunyikan kerumitan ini bukanlah yang Anda inginkan karena Anda menginginkan info maksimum tentang kegagalan. Konteks ekspektasi memungkinkan Anda untuk memberikan konteks ini secara manual. Saya kira tidak ideal, semacam konteks otomatis akan lebih baik, tetapi itu adalah satu-satunya cara yang saya lihat sekarang.

Ketika saya memecahkan sesuatu dan gagal, dengan Jest saya harus men-debugnya secara manual atau menambahkan logging atau _modifications._ apa pun yang jauh lebih tidak nyaman daripada hanya melihat hasil uji coba.
Di Jasmine misalnya, kami memiliki kemampuan untuk mencetak beberapa konteks agar lebih masuk akal tentang kegagalan.
Dalam kerangka pengujian paling populer Java, JUnit, kami juga memiliki fitur yang sama persis.

Maaf jika saya salah, tapi saya tidak melihat _technological_ counter-arguments untuk fitur ini di sini. Dan hal-hal seperti "ini tidak boleh ditambahkan karena tidak akan terlihat bagus" hanya konyol.

Bisakah kita membuka kembali? Bahkan jest.debug() seperti yang disarankan oleh @aaronabramov di atas akan sangat membantu saya.

This:

it('has all the methods', () => {
    since('cookie is a method', () => expect(reply.cookie).toBeDefined());
});

can be supported by adding this:


// setupTestFrameworkScriptFile.js
// http://facebook.github.io/jest/docs/configuration.html#setuptestframeworkscriptfile-string
global.since = (explanation, fn) => {
    try {
        fn();
    } catch(e) {
        e.message = explanation + '\n' + e.message;
        throw e;
    }
};

Juga, pesan melati-kustom terlihat mirip dengan apa yang diminta:

describe('test', function() {
  it('should be ok', function() {
    since(function() {
      return {'tiger': 'kitty'};
    }).
    expect(3).toEqual(4); // => '{"tiger":"kitty"}'
  });
});

Apakah ada rencana untuk membuka kembali ini? Sepertinya ada duplikat masalah ini baru-baru ini. Saya juga ingin menampilkan pesan khusus saat pengujian gagal.

Hmm.. Ini juga sesuatu yang saya punya di wishlist saya. Setelah membaca utas ini, saya dapat memahami respons yang berkaitan dengan penggunaan Jest di Facebook dan tidak ingin memengaruhi alur kerja Anda sendiri, tetapi ada beberapa saran yang tidak akan mengganggu pengujian yang ada, dan akan menambahkan fungsionalitas yang diinginkan oleh beberapa orang lain. punya (termasuk saya).

Apa yang diperlukan untuk format 2nd arg to expect() atau Since() untuk diterima sebagai PR? Saya bersedia menyumbangkan waktu untuk membantu ini.

Saya baru saja membaca utas dan melihat argumen yang bagus di kedua sisi. Saya pasti ingin mekanisme untuk memberikan pesan kesalahan khusus untuk alasan yang sama @timoxley awalnya diposting. Saat ini saya sedang melakukan sesuatu seperti ini:

import assert from 'assert'
import chalk from 'chalk'

test('api works', () => {
  assert.deepEqual(
    api(),
    [],
    chalk.red('api without magic provides no items')
  )
  assert.deepEqual(
    api(0),
    [],
    chalk.red('api with zero magic also provides no items')
  )
  assert.deepEqual(
    api(true),
    [1, 2, 3],
    chalk.red('api with magic enabled provides all items')
  )
})

Ini benar-benar bekerja dengan sangat baik, tetapi saya ingin menghindari keharusan menggunakan kapur untuk mendapatkan warna merah (mencetak tanpa warna sebaliknya) dan saya lebih suka ini didukung oleh expect .

Saya tidak terlalu peduli bagaimana itu diterapkan dengan jujur. Tapi inilah alternatif untuk since hanya untuk membuang sesuatu yang lain di luar sana jika orang lain lebih menyukainya:

const expectWithMessage = expect.withMessage(
  'api with magic enabled provides all items'
)
expectWithMessage(api(true)).toEqual([1, 2, 3])

// could be rewritten like
expect
  .withMessage('api with magic enabled provides all items')(api(true))
  .toEqual([1, 2, 3])

Saya tidak yakin saya menyukainya lebih baik dari since . Saya baik dengan apa pun, saya hanya benar-benar ingin memiliki ini :)

Oh, dan untuk menanggapi komentar:

Alasan mengapa saya pikir ini tidak terlalu berguna adalah karena para insinyur tidak ingin membuang waktu untuk menulis tes. Apa pun yang kita lakukan untuk mempersulit mereka hanya akan mengarah pada ujian yang lebih buruk.

Saya setuju bahwa kami tidak ingin mempersulit penulisan tes. Itu sebabnya ini akan menjadi perubahan tambahan. Jadi orang-orang yang tidak ingin "membuang waktu" membuat pengujian mereka lebih mudah untuk di-debug, mereka dapat melewatkan pesan yang bermanfaat, tetapi kemudian mereka akan masuk ke basis kode yang memiliki pesan bermanfaat seperti ini dan kemudian mereka akan berterima kasih kepada insinyur yang meluangkan waktu untuk menjelaskan pernyataan itu sedikit :wink:

Hai @cpojer ada pembaruan tentang ini?

Jest berfungsi dengan baik untuk saya kecuali untuk masalah ini ... Saat ini saya sedang berjuang untuk memperbaiki pernyataan yang gagal dalam perulangan for seperti
expectationsArray.forEach(expectation => expect(...))

Sulit untuk mengetahui dengan tepat harapan mana yang gagal tanpa pesan kesalahan khusus (kecuali saya salah melakukannya ..?)

Terima kasih

@mj-airwallex Anda baik untuk membungkus harapan dengan test dalam for loop misalnya:

const expectationsArray = [[0, 'a'], [1, 'b']];

expectationsArray.forEach(([expectation, desc]) => {
  test(`test ${desc}`, () => {
    expect(expectation).toBeGreaterThanOrEqual(2);
  });
});

Saya juga memiliki masalah dengan lelucon karena kebutuhan untuk memberikan pesan khusus selama harapan. Membungkus ekspektasi di bawah test tampaknya berfungsi untuk kasus di mana tidak perlu panggilan asinkron. Tetapi karena Jest tidak dapat menangani describe (#2235 ) mengembalikan janji, kami tidak dapat membuat pengujian dengan panggilan asinkron bersama dengan membungkusnya dengan test . Dan kita tidak dapat memiliki banyak test bersarang.

Berikut adalah contoh untuk menggambarkan masalah:

async function getArray() {
  return [0,0,0,0,0,0]
}

describe('Custom messages with async', async () => {
  const array = await getArray();
  array.forEach((item) => {
    test(`test${item}`, () => {
      expect(item).toBe(0)
    });
  });
})

Ada ide bagaimana menangani ini?

Melihat masalah di OP ("Akan menyenangkan jika ada beberapa konteks yang dapat dibaca manusia yang segera memperjelas harapan mana yang gagal"), saya pikir itu sudah terpecahkan sekarang. Pada Jest 22 kami mencetak konteks pernyataan yang gagal. Apakah pesan tambahan masih diperlukan? Jika _is_, itu bisa berupa komentar kode di atas atau di samping pernyataan

image

Penjelasan Async adalah masalah lain (yang tidak akan terbantu oleh kerangka kode yang ditambahkan)

Saya pikir saya tidak akan menggunakan deskripsi async dan sebagai gantinya menggunakan beforeEach atau beforeAll

@kentcdodds dapatkah Anda memberikan contoh bagaimana menangani ini dengan beforeEach atau beforeAll ? Jika Anda mencoba membangun semua panggilan asinkron yang diperlukan, menghasilkan beforeEach dan beforeAll pada akhirnya akan memaksa Anda untuk membuat test bersarang yang tidak diizinkan.

Ah, saya melewatkan apa yang Anda lakukan dengan panggilan asinkron itu. Maaf tentang itu Ya, Anda tidak dapat melakukan beforeEach atau beforeAll untuk melakukan itu.

@SimenB , mencetak konteks sudah banyak membantu dan menyelesaikan sebagian besar masalah dengan pesan khusus. Terima kasih untuk ini! Tetapi akan lebih baik untuk memiliki kemungkinan untuk pesan khusus secara eksplisit sebagai argumen karena membantu dalam situasi seperti menggunakan harapan dalam loop.

Melihat masalah di OP ("Akan menyenangkan jika ada beberapa konteks yang dapat dibaca manusia yang segera memperjelas harapan mana yang gagal"), saya pikir itu sudah terpecahkan sekarang.

Ya, ini menyelesaikan masalah asli, selama Anda memiliki akses ke sumber asli sebelum transpilasi, yang akan dimiliki sebagian besar pengguna Jest. IMO itu agak berat dibandingkan dengan mengizinkan pengguna untuk hanya mencetak pesan yang disediakan pengguna dengan kegagalan, tapi saya kira cukup bagus.

Baru mulai menggunakan Jest dan saya kehilangan fitur seperti ini. Kasus penggunaan saya:
Tes gagal di mana saya menegaskan properti suatu objek sebagai kebenaran. Ini akan membantu saya memahami kegagalan lebih cepat jika saya bisa mencatat objek jika pernyataan gagal.

Anda dapat menggunakan toHaveProperty untuk itu.

test('property', () => {
  expect({foo: 'bar'}).toHaveProperty('baz', 'foobar');
});

image

Jika Anda hanya ingin memeriksa apakah itu ada, lepaskan argumen kedua. Jika Anda hanya ingin menegaskan bahwa ia memiliki nilai _some_, Anda dapat menggunakan expect.anything() .
toMatchObject adalah alternatif lain.

Anda juga dapat menggunakan assert jika Anda mau.

test('property', () => {
  const obj = {foo: 'bar'};
  assert.equal(obj.baz, 'foobar', JSON.stringify(obj));
});

image

Terima kasih atas tipnya. assert.equal(obj.baz, 'foobar', JSON.stringify(obj)); akan melakukan pekerjaan dalam kasus khusus saya.

@SimenB @mpseidel apa yang ditegaskan? apakah itu perpustakaan pihak ketiga? Saya tidak dapat menemukan apa pun di dokumen lelucon.

@sharikovvladislav assert adalah modul inti simpul https://nodejs.org/api/assert.html

@mpseidel ups! Aku tidak tahu. Terima kasih. Berhasil.

Saya menggunakan fragmen kode berikut untuk meretas batasan kerangka kerja ini (dalam TypeScript tetapi cukup hapus anotasi tipe untuk JS)
export const explain = (expectation: () => void, explanation: string) => { try { expectation(); } catch(e) { console.log(explanation) throw e; } }

Hai,
Saya terkejut bahwa belum ada yang menyebutkan loop. Pesan tidak hanya berupa string, tetapi string dinamis tergantung pada iterasi loop.
jest-plugin-context bagus, terima kasih untuk ini bekerja, tapi agak berat dan masalah awal masih relevan imo.
Lihat tes ini

describe('MyStuff', () => {
    it('should render and contain relevant inputs', () => {
      const wrapper = shallowWrapped(<MyStuff />);
      const expectedKeys = ['love','jest','but','need','details','for','expect'];
      expectedKeys.forEach((key) => {
        expect(wrapper.find({ id: key }).length).toEqual(1);
      });
    });
  });

Semoga berhasil menemukan pelakunya. Saat ini saya harus menambahkan baris atau menguji objek seperti {len:.., key:..} , ini tidak bersih dan tidak ramah pengguna.
Saya pikir kasus penggunaan ini relevan, untuk formulir dan pemeriksaan rendering item misalnya.
Sintaksnya bisa sesederhana toEqual(1).context("my message") atau toEqual(1, "my message") (walaupun tentu saja saya tahu bahwa implementasi selalu lebih sulit, dan saya menghargai pekerjaan hebat yang Anda lakukan dengan Jest).

Mungkin menggunakan format yang sama seperti yang dilakukan chai - yaitu menambahkan pesan sebagai argumen kedua ke panggilan yang diharapkan:

expect(foo, 'this detail').toEqual(2)

T

Digunakan melati sebelumnya, jadi datang ke sini untuk menemukan itu tidak didukung.
Namun afik hal-hal ini adalah semua fungsi. Bisakah kita tidak hanya melakukan sesuatu seperti:

describe('MyStuff', () => {
    describe('should render and contain relevant inputs', () => {
      const wrapper = shallowWrapped(<MyStuff />);
      const expectedKeys = ['love','jest','but','need','details','for','expect'];

      expectedKeys.forEach((key) => {
        it(`contains key "${key}"`, () =>
          expect(wrapper.find({ id: key }).length).toEqual(1)
        )
      })
  });
});

2018-04-18-222246_646x390_scrot

@akkerman Solusi yang bagus. Karena deskripsi dan itu adalah global ajaib yang disediakan oleh lelucon, saya harus mengakui bahwa mereka bisa terasa tidak jelas, saya tidak yakin menulis t` dalam satu lingkaran bisa berhasil.

Bagaimana dengan merantai pengubah lain?

expect(foo).toEqual(bar).because('reason with %s placeholders')

Atau mungkin fungsi

expect(foo).toEqual(bar).explainedBy((result) => `Lorem ipsum ${result}`)

Saya pikir pengubah lain dengan cepat menjadi tidak terbaca.
T

2018-04-19 13:47 GMT+02:00 • Geovani de Souza [email protected] :

Bagaimana dengan merantai pengubah lain?

harapkan(foo).toEqual(bar).because('alasan dengan %s placeholder')

Atau mungkin fungsi

harapkan(foo).toEqual(bar).explainedBy((result) => Lorem ipsum ${result} )


Anda menerima ini karena Anda berkomentar.
Balas email ini secara langsung, lihat di GitHub
https://github.com/facebook/jest/issues/1965#issuecomment-382705387 , atau bisukan
benang
https://github.com/notifications/unsubscribe-auth/AAM5PwBCvET1KdEDeDEF7gGo708Naj8oks5tqHlSgaJpZM4Kc6Uu
.

--


Tarjei Huse
Mobil: 920 63 413

Cara kerja expect adalah dengan melempar, jadi itu tidak akan berhasil.

Saya pikir expect(something, 'some helpful text on failure').toEqual(somethingElse) atau expect.context(something, 'some helpful text on).toEqual(somethingElse) adalah alternatif terbaik, tetapi saya tidak terlalu menyukai keduanya

ini bisa dibuka kembali? Tampaknya Jest masih tidak memiliki solusi yang baik untuk menguji bagaimana status berubah melalui beberapa interaksi, misalnya:

  • menguji bagaimana wadah React stateful berubah sebagai respons terhadap serangkaian peristiwa
  • menguji bagaimana halaman web berubah selama beberapa interaksi menggunakan Dalang

Kedua kasus ini memerlukan melakukan serangkaian tindakan, dan menegaskan bagaimana keadaan berubah (atau tidak berubah) setelah setiap tindakan, jadi pengujian multi-pernyataan terkadang diperlukan. Tidak selalu mungkin untuk menyelesaikan masalah seperti ini dengan beforeEach .

Saya terus menemukan situasi di mana ini akan sangat berguna. Khususnya ketika saya menjalankan banyak interaksi dan pernyataan seperti yang dijelaskan oleh @callumlocke .

Jika kami menemukan API yang tidak dibenci orang, apakah ini sesuatu yang ingin Anda kejar? Saya benar-benar berpikir ini akan menjadi fitur yang berharga dan banyak digunakan.

Berikut ringkasan solusi yang diusulkan:

expect(api()).toEqual([]) // api without magic provides no items
it('api without magic provides no items', () => expect(api()).toEqual([]))
test('api without magic provides no items', () => expect(api()).toEqual([]))
expect(api()).toHaveNoItems()

expect(api(), 'api without magic provides no items').toEqual([])
expect(api()).because('api without magic provides no items').toEqual([])
since('api without magic provides no items').expect(api()).toEqual([]))
because('api without magic provides no items').expect(api()).toEqual([]))
jest.debug('api without magic provides no items'); expect(api()).toEqual([]))

Perhatikan bahwa .because() tambahan tidak dimungkinkan, jadi tidak disertakan sebagai opsi.

Keempat opsi di grup pertama didukung hari ini. Secara pribadi, saya menemukan bahwa opsi pertama (bingkai kode dengan komentar) berfungsi dengan baik. Dan bahkan lebih baik dari itu menggunakan pencocokan khusus (opsi 4).

Saya pikir apa yang perlu kita pahami untuk bergerak dalam hal ini adalah: apa yang lebih menarik tentang opsi di grup kedua daripada opsi di grup pertama? Apa yang ditambahkan grup kedua yang dapat membenarkan pemeliharaan inti untuk semua pencocokan yang kami sediakan (di pencocokan asinkron, pencocokan asimetris, pencocokan mata-mata, pencocokan lempar, pencocokan janji, dan pencocokan khusus)?

Hai,
Anda pada dasarnya mendapatkan beberapa kasus penggunaan:

  • Tes beberapa pernyataan (Anda perlu menegaskan banyak hal dalam sebuah tes)
  • Konteks pernyataan yang dihasilkan secara dinamis (Anda ingin variabel dalam pesan kegagalan Anda membuatnya lebih jelas, misalnya untuk mencetak bidang tertentu dari objek Anda yang gagal karena Anda mendapat banyak tes)
  • Pernyataan yang dihasilkan secara dinamis (Anda membuat loop yang menghasilkan pernyataan)

Opsi grup pertama terutama dimaksudkan untuk kasus penggunaan pertama. Jika Anda membutuhkan pernyataan yang dibuat secara dinamis, seperti yang diusulkan, Anda dapat membuat panggilan ke it dan test , sehingga pengujian dapat menghasilkan pengujian baru dalam satu lingkaran. Masalahnya adalah Anda menghasilkan tes dan bukan pernyataan . Bayangkan saya ingin menegaskan sesuatu pada setiap elemen dari array 1000 elemen, ini akan mengasapi ringkasan pengujian.

Karena kasus penggunaan dinamis itu masih jarang, kami harus tetap berpegang pada solusi yang membutuhkan kerja minimal untuk pengelola. Saya pribadi menyukai solusi because/since , karena kedengarannya cukup sederhana. Saya kira implementasinya sebagian besar akan membungkus expect dalam try/catch yang mencetak pesan dan mengembalikannya?
jest.debug terdengar aneh, bagi saya debugging adalah mencetak pesan meskipun tes benar-benar lulus
Opsi "argumen terakhir" juga bagus tetapi saya tidak yakin apakah itu bisa dilakukan karena expect dapat menerima sejumlah argumen yang bervariasi?

Saya baik-baik saja dengan opsi apa pun. Saya hanya ingin fiturnya. Saya juga tidak hebat dalam jest.debug API, tetapi jika itu satu-satunya yang masuk akal, saya tidak masalah karena saya hanya menginginkan fitur ini.

@kentcdodds bagaimana dengan empat opsi yang ada?

@eric-burel apakah Anda melihat test.each dan describe.each ditambahkan di Jest 23 (tersedia sebagai standalone untuk Jest <23)?

Seperti yang saya katakan, saya tidak terlalu peduli dengan opsi mana yang kami pilih. Saya hanya ingin fitur itu ada. Saya kira jika saya mengurutkannya berdasarkan urutan preferensi, itu akan menjadi:

  1. expect(api(), 'api without magic provides no items').toEqual([])
  2. because('api without magic provides no items').expect(api()).toEqual([]))
  3. since('api without magic provides no items').expect(api()).toEqual([]))
  4. expect(api()).because('api without magic provides no items').toEqual([])
  5. jest.debug('api without magic provides no items'); expect(api()).toEqual([]))

(test|describe).each bagus, tetapi tidak menyelesaikan masalah di mana Anda ingin memiliki banyak tindakan/pernyataan dalam satu pengujian.

Fitur ini memang ada saat ini dengan empat opsi:

expect(api()).toEqual([]) // api without magic provides no items
it('api without magic provides no items', () => expect(api()).toEqual([]))
test('api without magic provides no items', () => expect(api()).toEqual([]))
expect(api()).toHaveNoItems()

Apa yang salah dengan ini? Solusi _new_ yang diusulkan tampaknya hanya sedikit lebih baik daripada solusi yang ada ini. Manfaat apa yang mereka bawa dari apa yang kita miliki yang membenarkan biaya pemeliharaan?

@rickhanlonii Bagus Saya tidak tahu tentang test.each , itu benar-benar fitur yang hebat, terima kasih telah menunjukkan ini. Saya pikir itu memecahkan masalah untuk kasus penggunaan ke-3 saya, tes yang dihasilkan secara dinamis dari sebuah array.

Jadi itu meninggalkan yang kedua yang saya daftarkan: memiliki pesan kegagalan yang dihasilkan secara dinamis, yang akan membuat debugging lebih cepat. Saya tidak punya banyak kasus penggunaan sekarang, mungkin ketika Anda menguji nilai bidang objek, Anda ingin mencetak seluruh objek pada kegagalan. Itu sah-sah saja, karena apa pun yang membuat tes menulis lebih mudah, bahkan jika marjinal atau sedikit berlebihan. Lagi pula kita berdua bisa menulis it dan test , kecuali ada perbedaan yang saya tidak tahu tentang ini sebagian besar untuk kenyamanan.
Ini marjinal tetapi benar-benar sesuatu yang diharapkan (tidak ada permainan kata-kata) oleh pengguna, seperti yang ditunjukkan utas ini.

Sunting: membuat tes di tes lain dengan it atau test dan nama yang dihasilkan secara dinamis untuk tes adalah solusi yang valid tetapi saya benar-benar tidak suka membuat tes ketika maksud saya membuat pernyataan . Saya tidak akan pernah menduga itu mungkin jika solusi belum diberikan di utas ini.

Ini gila. Cukup tambahkan parameter opsional kedua ke expect(). Kami yang ingin menggunakannya akan (selektif), dan mereka yang tidak, tidak akan.

Mocha telah melakukan ini selamanya... itu salah satu alasan saya meninggalkan Jasmine bertahun-tahun yang lalu (yang lain adalah waktu yang jauh lebih baik mengejek.) Jika saya tidak harus bergabung dengan kereta musik React, saya tidak akan menggunakan Jest atau apapun turunan melati lainnya.

Mencetak pesan kesalahan adalah konvensi di banyak kerangka pengujian lainnya dan saya terkejut tidak melihatnya di Jest. Saya telah menemukan banyak contoh bermanfaat di utas ini (terima kasih untuk itu), tetapi menambahkan cara eksplisit untuk mencetak kesalahan khusus pada kegagalan pengujian akan menjadi tambahan yang bagus untuk kegunaan Jest. Ini akan memudahkan pengembang yang terbiasa dengan kerangka kerja pengujian lain (termasuk yang non-JS) untuk meningkatkan Jest.

@mattphillips menurut Anda mungkinkah melakukan sesuatu yang mirip dengan rantai lelucon di sini untuk memungkinkan solusi ada di userland? Misalnya argumen kedua untuk expect

Sejujurnya, ini adalah sesuatu yang sangat standar di sebagian besar kerangka kerja pengujian JS. Sangat kecewa tidak menemukannya di Jest karena kami menulis semua pengujian kami dengan pesan kesalahan khusus.

@SimenB maaf saya baru melihat pesan Anda pagi ini!

Ya ini bisa dilakukan di userland, saya baru saja mengetuknya dan merilisnya sebagai jest-expect-message https://github.com/mattphillips/jest-expect-message

Umpan balik diterima :senyum:

Luar biasa, terima kasih telah melakukannya!

@cpojer

Alasan mengapa saya pikir ini tidak terlalu berguna adalah karena para insinyur tidak ingin membuang waktu untuk menulis tes. Apa pun yang kita lakukan untuk mempersulit mereka hanya akan mengarah pada ujian yang lebih buruk.

Dua hal:

  1. Menambahkan argumen kedua opsional ke expect() hampir tidak mempersulit pengembang.
  2. Hal terakhir yang ingin dilakukan pengembang adalah membuang waktu untuk men-debug apa yang menyebabkan pengujian gagal. Seringkali mengharapkan vs diterima adalah cara yang baik untuk memeriksa apakah suatu kondisi terpenuhi, tetapi seringkali tidak cukup konteks tentang apa yang menyebabkannya gagal.

Saya menggunakan Mocha/Chai, serta tape, sebelum datang ke Jest, dan ini benar-benar pemecah kesepakatan. Apa yang harus kami lakukan untuk mendapatkan dukungan pesan khusus sesuai harapan?

Memberi tahu kami untuk expect.extend untuk membuat pencocokan khusus terdengar persis seperti apa yang Anda coba hindari dalam argumen pertama Anda: "insinyur tidak ingin membuang waktu untuk menulis tes."

Saya merasa mudah untuk membuka unit test dan melihat nomor baris, sehingga kasus penggunaan di OP tidak mengganggu saya.

Kasus penggunaan yang mengganggu saya adalah ketika saya memiliki loop di dalam tes, misalnya untuk menguji setiap nilai enum, misalnya seperti ini:

it("Should contain at least one word of every wordType", () => {
  for (const wordType of wordTypes) {
    expect(words.find((word) => word.wordType === wordType)).toBeTruthy();
  }
});

Jika gagal maka saya tidak tahu nilai WordType mana yang gagal.

Solusi saya adalah menggantinya dengan pesan yang berisi hasil tes, dan berharap pesan tersebut berisi hasil tes yang diharapkan (yaitu benar). Jika gagal maka Jest mencetak pesan yang berisi informasi tambahan.

    expect(`${wordType} ${!!words.find((word) => word.wordType === wordType)}`).toEqual(`${wordType} ${true}`);

Jest mencetak ini ...

expect(received).toEqual(expected)

Difference:

- Expected
+ Received

- 6 true
+ 6 false

... yang memberi tahu saya bahwa wordType adalah 6 ketika gagal.

Atau lebih mudah dibaca sesuatu seperti ...

    if (!words.find((word) => word.wordType === wordType)) {
      expect(`Didn't find a word with wordType '${wordType}'`).toEqual(null);
    }

@cwellsx periksa tes parameter dengan test.each dengan cara ini setiap WordType akan menjadi tes independen dengan judul deskriptif (berdasarkan nilainya)

Ini akan sangat berguna dalam tes seperti ini:

test("compare ArrayBufferCursors", () => {
    const orig: ArrayBufferCursor;
    const test: ArrayBufferCursor;

    expect(test.size).toBe(orig.size);

    while (orig.bytes_left) {
        expect(test.u8()).toBe(orig.u8());
    }
});

Saat ini saya hanya tahu beberapa byte di ArrayBufferCursor salah tetapi saya tidak tahu yang mana. Mampu menambahkan indeks sebagai konteks akan membuat debugging lebih mudah. Untungnya beberapa orang telah memberikan solusi di sini tetapi semuanya jelek dan lambat.

@rickhanlonii , saya mengerti bahwa Anda ingin mengurangi biaya pemeliharaan lelucon, tetapi opsi yang Anda tawarkan dalam komentar Anda meningkatkan pemeliharaan unit test dari semua proyek lainnya. Jika saya ingin menjelaskan pernyataan menggunakan toEqual , yang sebaliknya cukup sempurna, apakah Anda benar-benar menyarankan saya untuk memperkenalkan pencocokan khusus?!

Meskipun ada kasus dengan pengujian berulang, di mana it.each berguna, harus menambahkan kode yang tidak perlu untuk komentar sederhana pada pernyataan membuat kerangka pengujian ini lebih berat untuk digunakan.

Diskusi dan kemunculan jest-expect-message ini mengingatkan saya tentang masalah beforeAll di Jasmine...

Saya mungkin salah memahami permintaan OP di sini tetapi masalah yang saya coba selesaikan dan membawa saya ke utas masalah ini diselesaikan hanya dengan menggunakan try / catch sederhana dan membungkus jest.expect . Yang ingin saya lakukan hanyalah mencatat keseluruhan objek yang saya harapkan vs yang saya terima + beberapa log dasar yang menjelaskan signifikansinya. Tentu saja, ini dapat diperluas untuk melakukan apa saja yang Anda inginkan.

Versi paling umum dari ini mungkin terlihat seperti:

in some test utility file...

const myExpect = (expectFn, errorCallback) => {
  try {
    expectFn();
  } catch (err) {
    errorCallback();
    throw new Error(err);
  }
};

// in the actual test suite...

const context = { hello: "world", results, expected };
myExpect(
    () => expect(results).toEqual(expected),
    () => console.error("[Error] -- context:", JSON.stringify(context))
);

+1
Hai, saya juga sangat menyukai fitur ini. Itu akan membuat hidup saya jauh lebih mudah.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat