2015-07-04 20 views
8

W jaki sposób zapewniamy równość map i zestawów ES6?Testowanie map/zestawów za pomocą QUnit (lub innego narzędzia testowego)

Na przykład:

// ES6 Map 
var m1 = new Map(); 
m1.set('one', 1); 
var m2 = new Map(); 
m2.set('two', 2); 
assert.deepEqual(m1,m2);  // outputs: passed. 

// ES6 Set 
var s1 = new Set(); 
s1.add(1); 
var s2 = new Set(); 
s2.add(2); 
assert.deepEqual(s1,s2);  // outputs: passed. 

Zamiarem jest, aby twierdzić, że elementy Zestawów/Mapy są równe. Oba te twierdzenia powinny zakończyć się niepowodzeniem.

Czy istnieje odpowiednik deepEqual dla zestawów/map? Innymi słowy, bez ręcznego powtarzania elementów, w jaki sposób możemy dokładnie testować równość Set/Map?

Jeśli w QUnit nie ma możliwości, czy istnieje narzędzie do testowania jednostek, które działa dla zestawów i map ES6?

Edit

w Firefox, który obsługuje Array.from(), byłem porównując zestawy i mapy przez:

assert.deepEqual(Array.from(m1), Array.from(m2)); 

Ale to nie działa z innych przeglądarek, które nie obsługują Array.from(). Nawet z polyfillem Array.from, Chrome/IE nie działa - Array.from(set) zawsze tworzy pustą tablicę niezależnie od ustawionej zawartości. Jest to prawdopodobnie spowodowane brakiem obsługi tych przeglądarek dla standardowych iteracji.

Po drugie, zredukowanie go do porównania tablic może nie zawsze być odpowiednie. Chcemy skończyć z tym, co uważam za fałszywe alarmy:

var s = new Set(); 
s.add([1,2]); 
var m = new Map(); 
m.set(1,2); 
assert.deepEqual(Array.from(s), Array.from(m)); // outputs: passed. 

Aktualizacja:

Plaster jest obecnie w pracach na QUnit przedłużyć deepEqual() obsłużyć ES6 zestawy i Maps. Po scaleniu tego żądania ściągnięcia, powinniśmy móc użyć deepEqual() do porównania zestawów i map. (-:

+0

Ponieważ jak już wiesz Quint nie zrobi to za Ciebie, Twoje pytanie jest ograniczona do "Czy istnieje narzędzie do testowania jednostek, które rozumie zestawy i mapy ES6" (co prawdopodobnie powinno być tytułem), które nie jest "dobrym pytaniem" według zasad SO. Ewentualnie możesz poszukać odpowiedzi opartej na kodzie na swoje potrzeby, ale aktywnie odrzucasz taką odpowiedź. Więc jakiej odpowiedzi szukasz? – Amit

+0

Nie wiem, że QUnit nie zadziała. Może być jakiś sposób, aby to zrobić, może nie przez "deepEqual". Nie jestem na tyle arogancki, aby złożyć takie oświadczenie jak "QUnit will not work", stąd pytanie. I nie odrzucam aktywnie odpowiedzi opartych na kodach. Chciałem tylko podkreślić, co robię w QUnit jako przybliżenie i jakie problemy to powoduje. Odpowiedź, której szukam: czy istnieje sposób na porównanie zestawów/map w QUnit? Jeśli nie, co mogę zrobić? – light

Odpowiedz

2

porównanie Wyczerpujące Map za pomocą funkcji wyższego rzędu

mam zamiar podejść do tego w ten sam sposób ja zbliżył porównania tablicy w tym podobną odpowiedź: How to compare arrays in JavaScript?

mam zamiar przejść przez kodzie bit po bicie, ale będę mieć pełną runnable przykład pod koniec


Płytki porównanie

Po pierwsze, zaczniemy od ogólnej funkcji porównywania map. W ten sposób możemy zrobić wszelkiego rodzaju porównań na obiekty mapy, nie tylko testowanie równości

to mapCompare funkcja zgadza się z naszej intuicji na temat Mapy powinny być porównywane - porównujemy każdy klawisz z m1 wobec każdego klawisza od m2. Uwaga, ten konkretny komparator robi płytkie porównanie.Będziemy obsługiwać głębokie porównania w chwilę

const mapCompare = f => (m1, m2) => { 
    const aux = (it, m2) => { 
    let {value, done} = it.next() 
    if (done) return true 
    let [k, v] = value 
    return f (v, m2.get(k), $=> aux(it, m2)) 
    } 
    return aux(m1.entries(), m2) && aux(m2.entries(), m1) 
} 

Jedyną rzeczą, która może nie być od razu jasne jest $=> aux(it, m2) thunk. Mapy mają wbudowany generator, .entries(), a my możemy skorzystać z leniwej oceny, zwracając wczesną odpowiedź false, gdy tylko zostanie znaleziona niesparowana para klucz/wartość. Oznacza to, że musimy napisać nasze komparatory w nieco szczególny sposób.

const shortCircuitEqualComparator = (a, b, k) => 
    a === b ? true && k() : false 

a i bwartości z m1.get(somekey) i m2.get(somekey) odpowiednio. iff dwie wartości są ściśle równe(), tylko wtedy chcemy kontynuować porównanie - w tym przypadku zwracamy true && k(), gdzie k() jest pozostałą częścią porównania para klucz/wartość. Z drugiej strony, jeśli a i b nie pasują, możemy wrócić wcześnie false i pominąć porównując resztę wartości - czyli już wiemy, że m1 i m2 nie pasują jeśli dowolnya/b pary NIE mecz.

Wreszcie, możemy zdefiniować mapEqual - to również proste. To właśnie mapCompare użyciu naszych specjalnych shortCircuitEqualComparator

const mapEqual = mapCompare (shortCircuitEqualComparator) 

Rzućmy okiem na to, jak działa

// define two maps that are equal but have keys in different order 
const a = new Map([['b', 2], ['a', 1]]) 
const b = new Map([['a', 1], ['b', 2]]) 

// define a third map that is not equal 
const c = new Map([['a', 3], ['b', 2]]) 

// verify results 
// a === b should be true 
// b === a should be true 
console.log('true', mapEqual(a, b)) // true true 
console.log('true', mapEqual(b, a)) // true true 

// a === c should be false 
// c === a should be false too 
console.log('false', mapEqual(a, c)) // false false 
console.log('false', mapEqual(c, a)) // false false 

Heck tak. Sprawy wyglądają dobrze ...


Głębokie porównań z Rick & Morty

Teraz mamy fricken słodki mapCompare rodzajowy, głębokie porównanie jest bardzo proste. Zauważ, że faktycznie implementujemy mapDeepCompare samemu za pomocą .

Używamy niestandardowego komparatora, który po prostu sprawdza, czy a i b są obiektami Map - jeśli tak, to powtarzamy korzystanie z mapDeepCompare w zagnieżdżonych Mapach; pamiętaj też, aby zadzwonić pod numer ... && k(), aby sprawdzić, czy pozostałe pary klucz/wartość są porównywane. Jeżeli a lub b jest obiektem non-Map, normalny porównanie robi używając f i mijamy kontynuację k wzdłuż bezpośrednio

const mapDeepCompare = f => mapCompare ((a, b, k) => { 
    console.log(a, b) 
    if (a instanceof Map && b instanceof Map) 
    return mapDeepCompare (f) (a,b) ? true && k() : false 
    else 
    return f(a,b,k) 
}) 

Teraz mapDeepCompare możemy wykonać dowolny rodzaj głębokiej porównania na zagnieżdżonych Maps . Pamiętaj, że równość to tylko jedna z rzeczy, które możemy sprawdzić.

Bez dalszych wyjaśnień, mapDeepEqual. Co ważne, uzyskujemy ponowne użycie naszego shortCircuitEqualComparator, które zdefiniowaliśmy wcześniej. To bardzo wyraźnie pokazuje, że nasze komparatory mogą być (ponownie) używane do płytkich porównań map lub.

const mapDeepEqual = mapDeepCompare (shortCircuitEqualComparator) 

Zobaczmy to działa

// define two nested maps that are equal but have different key order 
const e = new Map([ 
    ['a', 1], 
    ['b', new Map([['c', 2]])] 
]) 

const f = new Map([ 
    ['b', new Map([['c', 2]])], 
    ['a', 1] 
]) 

// define a third nested map that is not equal 
const g = new Map([ 
    ['b', new Map([ 
    ['c', 3] 
    ])], 
    ['a', 1] 
]) 

// e === f should be true 
// f === e should also be true 
console.log('true', mapDeepEqual(e, f)) // true 
console.log('true', mapDeepEqual(f, e)) // true 

// e === g should be false 
// g === e should also be false 
console.log('false', mapDeepEqual(e, g)) // false 
console.log('false', mapDeepEqual(g, e)) // false 

OK, a tylko upewnić. Co się stanie, jeśli zadzwonimy pod numer mapEqual w naszych zagnieżdżonych Mapach e i f? Od mapEqual robi płytkie porównanie, oczekujemy, że wynik powinien być false

console.log('false', mapEqual(e, f)) // false 
console.log('false', mapEqual(f, e)) // false 

I tam masz. Płytkie i głębokie porównanie obiektów mapy ES6. Można by napisać prawie identyczny zestaw funkcji do obsługi zestawu ES6. Zostawię to jako ćwiczenie dla czytelników.


kod Runnable demo

To jest cały kod powyżej zebrane w jednym runnable demo. Każde wywołanie console.log wyprowadza <expected>, <actual>. Tak więc true, true lub false, false byłoby testem przechodzącym - podczas gdy true, false byłby testem zakończonym niepowodzeniem.

// mapCompare :: ((a, a, (* -> Bool)) -> Bool) -> (Map(k:a), Map(k:a)) -> Bool 
 
const mapCompare = f => (m1, m2) => { 
 
    const aux = (it, m2) => { 
 
    let {value, done} = it.next() 
 
    if (done) return true 
 
    let [k, v] = value 
 
    return f (v, m2.get(k), $=> aux(it, m2)) 
 
    } 
 
    return aux(m1.entries(), m2) && aux(m2.entries(), m1) 
 
} 
 

 
// mapDeepCompare :: ((a, a, (* -> Bool)) -> Bool) -> (Map(k:a), Map(k:a)) -> Bool 
 
const mapDeepCompare = f => mapCompare ((a, b, k) => { 
 
    if (a instanceof Map && b instanceof Map) 
 
    return mapDeepCompare (f) (a,b) ? true && k() : false 
 
    else 
 
    return f(a,b,k) 
 
}) 
 

 
// shortCircuitEqualComparator :: (a, a, (* -> Bool)) -> Bool 
 
const shortCircuitEqualComparator = (a, b, k) => 
 
    a === b ? true && k() : false 
 

 
// mapEqual :: (Map(k:a), Map(k:a)) -> Bool 
 
const mapEqual = mapCompare (shortCircuitEqualComparator) 
 

 
// mapDeepEqual :: (Map(k:a), Map(k:a)) -> Bool 
 
const mapDeepEqual = mapDeepCompare (shortCircuitEqualComparator) 
 
    
 
// fixtures 
 
const a = new Map([['b', 2], ['a', 1]]) 
 
const b = new Map([['a', 1], ['b', 2]]) 
 
const c = new Map([['a', 3], ['b', 2]]) 
 
const d = new Map([['a', 1], ['c', 2]]) 
 
const e = new Map([['a', 1], ['b', new Map([['c', 2]])]]) 
 
const f = new Map([['b', new Map([['c', 2]])], ['a', 1]]) 
 
const g = new Map([['b', new Map([['c', 3]])], ['a', 1]]) 
 

 
// shallow comparison of two equal maps 
 
console.log('true', mapEqual(a, b)) 
 
console.log('true', mapEqual(b, a)) 
 
// shallow comparison of two non-equal maps (differing values) 
 
console.log('false', mapEqual(a, c)) 
 
console.log('false', mapEqual(c, a)) 
 
// shallow comparison of two other non-equal maps (differing keys) 
 
console.log('false', mapEqual(a, d)) 
 
console.log('false', mapEqual(d, a)) 
 
// deep comparison of two equal nested maps 
 
console.log('true', mapDeepEqual(e, f)) 
 
console.log('true', mapDeepEqual(f, e)) 
 
// deep comparison of two non-equal nested maps 
 
console.log('false', mapDeepEqual(e, g)) 
 
console.log('false', mapDeepEqual(g, e)) 
 
// shallow comparison of two equal nested maps 
 
console.log('false', mapEqual(e, f)) 
 
console.log('false', mapEqual(f, e))

+0

Dzięki. W rzeczywistości to właśnie robię, ale nie mogę oprzeć się wrażeniu, że to nie jest najlepsze podejście, ponieważ 'Array.from()' spowoduje zamiast tego porównanie tablic. Oznacza to, że mapa z elementem {1: 2} pomyślnie dopasuje zestaw z elementem [1, 2]. – light

+0

Nie, nie będzie. '[['1', 2]]! == [1,2]' – naomik

+0

Witam, zobacz część edycyjną pytania. Przetestowałem to na Firefox. – light

0

Właśnie stworzył bibliotekę (@nodeguy/assert), aby rozwiązać ten:

const assert = require('@nodeguy/assert') 

// ES6 Map 
var m1 = new Map(); 
m1.set('one', 1); 
var m2 = new Map(); 
m2.set('two', 2); 
assert.deepEqual(m1,m2);  // outputs: failed. 

// ES6 Set 
var s1 = new Set(); 
s1.add(1); 
var s2 = new Set(); 
s2.add(2); 
assert.deepEqual(s1,s2);  // outputs: failed. 
+0

Czy [żartujesz] (https://github.com/NodeGuy/assert/blob/master/lib/index.js#L10)? Masz na myśli, że właśnie utworzyłeś bibliotekę, która obmacuje ['assert.deepEqual'] (https://nodejs.org/api/assert.html#assert_assert_deepequal_actual_expected_message) za pomocą [' lodash.isEqual'] (https://lodash.com/ docs/4.17.4 # isEqual)? Jeśli zamierzasz używać programu Lodash, zdecydowanie lepiej byłoby użyć go bezpośrednio w testach - np. 'Assert.ok (_. IsEqual (m1, m2)) zamiast całkowicie zastępować' assert.deepEqual' węzła. – naomik

+0

Na dodatek, twoje testy nawet nie potwierdzają zachowania, które obiecujesz. Nie wspominając już o marności pisania testów na innych bibliotekach ... – naomik

+0

Nie, Lodash jest szczegółem implementacji, a 'assert.ok (_. IsEqual (m1, m2))' nie pokazuje wartości w komunikacie o błędzie w sposób 'assert.deepEqual' ma. Moje testy obsługują pierwszą sprawę, na której mi zależy, stąd wydanie "0.1.1". Jestem otwarty na wyciąganie wniosków, jeśli chcesz dodać więcej. –