Best Go-testdeep code snippet using td_test.Read
example_t_test.go
Source:example_t_test.go
...1061 if err != nil {1062 t.Fatal(err)1063 }1064 ok = t.JSON(got, file, []any{td.Tag("name", td.HasPrefix("Bob")), td.Tag("age", td.Between(40, 45)), td.Tag("gender", td.Re(`^(male|female)\z`))})1065 fmt.Println("Full match from io.Reader:", ok)1066 // Output:1067 // Full match from file name: true1068 // Full match from io.Reader: true1069}1070func ExampleT_JSONPointer_rfc6901() {1071 t := td.NewT(&testing.T{})1072 got := json.RawMessage(`1073{1074 "foo": ["bar", "baz"],1075 "": 0,1076 "a/b": 1,1077 "c%d": 2,1078 "e^f": 3,1079 "g|h": 4,1080 "i\\j": 5,1081 "k\"l": 6,1082 " ": 7,1083 "m~n": 81084}`)1085 expected := map[string]any{1086 "foo": []any{"bar", "baz"},1087 "": 0,1088 "a/b": 1,1089 "c%d": 2,1090 "e^f": 3,1091 "g|h": 4,1092 `i\j`: 5,1093 `k"l`: 6,1094 " ": 7,1095 "m~n": 8,1096 }1097 ok := t.JSONPointer(got, "", expected)1098 fmt.Println("Empty JSON pointer means all:", ok)1099 ok = t.JSONPointer(got, `/foo`, []any{"bar", "baz"})1100 fmt.Println("Extract `foo` key:", ok)1101 ok = t.JSONPointer(got, `/foo/0`, "bar")1102 fmt.Println("First item of `foo` key slice:", ok)1103 ok = t.JSONPointer(got, `/`, 0)1104 fmt.Println("Empty key:", ok)1105 ok = t.JSONPointer(got, `/a~1b`, 1)1106 fmt.Println("Slash has to be escaped using `~1`:", ok)1107 ok = t.JSONPointer(got, `/c%d`, 2)1108 fmt.Println("% in key:", ok)1109 ok = t.JSONPointer(got, `/e^f`, 3)1110 fmt.Println("^ in key:", ok)1111 ok = t.JSONPointer(got, `/g|h`, 4)1112 fmt.Println("| in key:", ok)1113 ok = t.JSONPointer(got, `/i\j`, 5)1114 fmt.Println("Backslash in key:", ok)1115 ok = t.JSONPointer(got, `/k"l`, 6)1116 fmt.Println("Double-quote in key:", ok)1117 ok = t.JSONPointer(got, `/ `, 7)1118 fmt.Println("Space key:", ok)1119 ok = t.JSONPointer(got, `/m~0n`, 8)1120 fmt.Println("Tilde has to be escaped using `~0`:", ok)1121 // Output:1122 // Empty JSON pointer means all: true1123 // Extract `foo` key: true1124 // First item of `foo` key slice: true1125 // Empty key: true1126 // Slash has to be escaped using `~1`: true1127 // % in key: true1128 // ^ in key: true1129 // | in key: true1130 // Backslash in key: true1131 // Double-quote in key: true1132 // Space key: true1133 // Tilde has to be escaped using `~0`: true1134}1135func ExampleT_JSONPointer_struct() {1136 t := td.NewT(&testing.T{})1137 // Without json tags, encoding/json uses public fields name1138 type Item struct {1139 Name string1140 Value int641141 Next *Item1142 }1143 got := Item{1144 Name: "first",1145 Value: 1,1146 Next: &Item{1147 Name: "second",1148 Value: 2,1149 Next: &Item{1150 Name: "third",1151 Value: 3,1152 },1153 },1154 }1155 ok := t.JSONPointer(got, "/Next/Next/Name", "third")1156 fmt.Println("3rd item name is `third`:", ok)1157 ok = t.JSONPointer(got, "/Next/Next/Value", td.Gte(int64(3)))1158 fmt.Println("3rd item value is greater or equal than 3:", ok)1159 ok = t.JSONPointer(got, "/Next", td.JSONPointer("/Next",1160 td.JSONPointer("/Value", td.Gte(int64(3)))))1161 fmt.Println("3rd item value is still greater or equal than 3:", ok)1162 ok = t.JSONPointer(got, "/Next/Next/Next/Name", td.Ignore())1163 fmt.Println("4th item exists and has a name:", ok)1164 // Struct comparison work with or without pointer: &Item{â¦} works too1165 ok = t.JSONPointer(got, "/Next/Next", Item{1166 Name: "third",1167 Value: 3,1168 })1169 fmt.Println("3rd item full comparison:", ok)1170 // Output:1171 // 3rd item name is `third`: true1172 // 3rd item value is greater or equal than 3: true1173 // 3rd item value is still greater or equal than 3: true1174 // 4th item exists and has a name: false1175 // 3rd item full comparison: true1176}1177func ExampleT_JSONPointer_has_hasnt() {1178 t := td.NewT(&testing.T{})1179 got := json.RawMessage(`1180{1181 "name": "Bob",1182 "age": 42,1183 "children": [1184 {1185 "name": "Alice",1186 "age": 161187 },1188 {1189 "name": "Britt",1190 "age": 21,1191 "children": [1192 {1193 "name": "John",1194 "age": 11195 }1196 ]1197 }1198 ]1199}`)1200 // Has Bob some children?1201 ok := t.JSONPointer(got, "/children", td.Len(td.Gt(0)))1202 fmt.Println("Bob has at least one child:", ok)1203 // But checking "children" exists is enough here1204 ok = t.JSONPointer(got, "/children/0/children", td.Ignore())1205 fmt.Println("Alice has children:", ok)1206 ok = t.JSONPointer(got, "/children/1/children", td.Ignore())1207 fmt.Println("Britt has children:", ok)1208 // The reverse can be checked too1209 ok = t.Cmp(got, td.Not(td.JSONPointer("/children/0/children", td.Ignore())))1210 fmt.Println("Alice hasn't children:", ok)1211 ok = t.Cmp(got, td.Not(td.JSONPointer("/children/1/children", td.Ignore())))1212 fmt.Println("Britt hasn't children:", ok)1213 // Output:1214 // Bob has at least one child: true1215 // Alice has children: false1216 // Britt has children: true1217 // Alice hasn't children: true1218 // Britt hasn't children: false1219}1220func ExampleT_Keys() {1221 t := td.NewT(&testing.T{})1222 got := map[string]int{"foo": 1, "bar": 2, "zip": 3}1223 // Keys tests keys in an ordered manner1224 ok := t.Keys(got, []string{"bar", "foo", "zip"})1225 fmt.Println("All sorted keys are found:", ok)1226 // If the expected keys are not ordered, it fails1227 ok = t.Keys(got, []string{"zip", "bar", "foo"})1228 fmt.Println("All unsorted keys are found:", ok)1229 // To circumvent that, one can use Bag operator1230 ok = t.Keys(got, td.Bag("zip", "bar", "foo"))1231 fmt.Println("All unsorted keys are found, with the help of Bag operator:", ok)1232 // Check that each key is 3 bytes long1233 ok = t.Keys(got, td.ArrayEach(td.Len(3)))1234 fmt.Println("Each key is 3 bytes long:", ok)1235 // Output:1236 // All sorted keys are found: true1237 // All unsorted keys are found: false1238 // All unsorted keys are found, with the help of Bag operator: true1239 // Each key is 3 bytes long: true1240}1241func ExampleT_Last_classic() {1242 t := td.NewT(&testing.T{})1243 got := []int{-3, -2, -1, 0, 1, 2, 3}1244 ok := t.Last(got, td.Lt(0), -1)1245 fmt.Println("last negative number is -1:", ok)1246 isEven := func(x int) bool { return x%2 == 0 }1247 ok = t.Last(got, isEven, 2)1248 fmt.Println("last even number is 2:", ok)1249 ok = t.Last(got, isEven, td.Gt(0))1250 fmt.Println("last even number is > 0:", ok)1251 ok = t.Last(got, isEven, td.Code(isEven))1252 fmt.Println("last even number is well even:", ok)1253 // Output:1254 // last negative number is -1: true1255 // last even number is 2: true1256 // last even number is > 0: true1257 // last even number is well even: true1258}1259func ExampleT_Last_empty() {1260 t := td.NewT(&testing.T{})1261 ok := t.Last(([]int)(nil), td.Gt(0), td.Gt(0))1262 fmt.Println("last in nil slice:", ok)1263 ok = t.Last([]int{}, td.Gt(0), td.Gt(0))1264 fmt.Println("last in empty slice:", ok)1265 ok = t.Last(&[]int{}, td.Gt(0), td.Gt(0))1266 fmt.Println("last in empty pointed slice:", ok)1267 ok = t.Last([0]int{}, td.Gt(0), td.Gt(0))1268 fmt.Println("last in empty array:", ok)1269 // Output:1270 // last in nil slice: false1271 // last in empty slice: false1272 // last in empty pointed slice: false1273 // last in empty array: false1274}1275func ExampleT_Last_struct() {1276 t := td.NewT(&testing.T{})1277 type Person struct {1278 Fullname string `json:"fullname"`1279 Age int `json:"age"`1280 }1281 got := []*Person{1282 {1283 Fullname: "Bob Foobar",1284 Age: 42,1285 },1286 {1287 Fullname: "Alice Bingo",1288 Age: 37,1289 },1290 }1291 ok := t.Last(got, td.Smuggle("Age", td.Gt(30)), td.Smuggle("Fullname", "Alice Bingo"))1292 fmt.Println("last person.Age > 30 â Alice:", ok)1293 ok = t.Last(got, td.JSONPointer("/age", td.Gt(30)), td.SuperJSONOf(`{"fullname":"Alice Bingo"}`))1294 fmt.Println("last person.Age > 30 â Alice, using JSON:", ok)1295 ok = t.Last(got, td.JSONPointer("/age", td.Gt(30)), td.JSONPointer("/fullname", td.HasPrefix("Alice")))1296 fmt.Println("first person.Age > 30 â Alice, using JSONPointer:", ok)1297 // Output:1298 // last person.Age > 30 â Alice: true1299 // last person.Age > 30 â Alice, using JSON: true1300 // first person.Age > 30 â Alice, using JSONPointer: true1301}1302func ExampleT_CmpLax() {1303 t := td.NewT(&testing.T{})1304 gotInt64 := int64(1234)1305 gotInt32 := int32(1235)1306 type myInt uint161307 gotMyInt := myInt(1236)1308 expected := td.Between(1230, 1240) // int type here1309 ok := t.CmpLax(gotInt64, expected)1310 fmt.Println("int64 got between ints [1230 .. 1240]:", ok)1311 ok = t.CmpLax(gotInt32, expected)1312 fmt.Println("int32 got between ints [1230 .. 1240]:", ok)1313 ok = t.CmpLax(gotMyInt, expected)1314 fmt.Println("myInt got between ints [1230 .. 1240]:", ok)1315 // Output:1316 // int64 got between ints [1230 .. 1240]: true1317 // int32 got between ints [1230 .. 1240]: true1318 // myInt got between ints [1230 .. 1240]: true1319}1320func ExampleT_Len_slice() {1321 t := td.NewT(&testing.T{})1322 got := []int{11, 22, 33}1323 ok := t.Len(got, 3, "checks %v len is 3", got)1324 fmt.Println(ok)1325 ok = t.Len(got, 0, "checks %v len is 0", got)1326 fmt.Println(ok)1327 got = nil1328 ok = t.Len(got, 0, "checks %v len is 0", got)1329 fmt.Println(ok)1330 // Output:1331 // true1332 // false1333 // true1334}1335func ExampleT_Len_map() {1336 t := td.NewT(&testing.T{})1337 got := map[int]bool{11: true, 22: false, 33: false}1338 ok := t.Len(got, 3, "checks %v len is 3", got)1339 fmt.Println(ok)1340 ok = t.Len(got, 0, "checks %v len is 0", got)1341 fmt.Println(ok)1342 got = nil1343 ok = t.Len(got, 0, "checks %v len is 0", got)1344 fmt.Println(ok)1345 // Output:1346 // true1347 // false1348 // true1349}1350func ExampleT_Len_operatorSlice() {1351 t := td.NewT(&testing.T{})1352 got := []int{11, 22, 33}1353 ok := t.Len(got, td.Between(3, 8),1354 "checks %v len is in [3 .. 8]", got)1355 fmt.Println(ok)1356 ok = t.Len(got, td.Lt(5), "checks %v len is < 5", got)1357 fmt.Println(ok)1358 // Output:1359 // true1360 // true1361}1362func ExampleT_Len_operatorMap() {1363 t := td.NewT(&testing.T{})1364 got := map[int]bool{11: true, 22: false, 33: false}1365 ok := t.Len(got, td.Between(3, 8),1366 "checks %v len is in [3 .. 8]", got)1367 fmt.Println(ok)1368 ok = t.Len(got, td.Gte(3), "checks %v len is ⥠3", got)1369 fmt.Println(ok)1370 // Output:1371 // true1372 // true1373}1374func ExampleT_Lt_int() {1375 t := td.NewT(&testing.T{})1376 got := 1561377 ok := t.Lt(got, 157, "checks %v is < 157", got)1378 fmt.Println(ok)1379 ok = t.Lt(got, 156, "checks %v is < 156", got)1380 fmt.Println(ok)1381 // Output:1382 // true1383 // false1384}1385func ExampleT_Lt_string() {1386 t := td.NewT(&testing.T{})1387 got := "abc"1388 ok := t.Lt(got, "abd", `checks "%v" is < "abd"`, got)1389 fmt.Println(ok)1390 ok = t.Lt(got, "abc", `checks "%v" is < "abc"`, got)1391 fmt.Println(ok)1392 // Output:1393 // true1394 // false1395}1396func ExampleT_Lte_int() {1397 t := td.NewT(&testing.T{})1398 got := 1561399 ok := t.Lte(got, 156, "checks %v is ⤠156", got)1400 fmt.Println(ok)1401 ok = t.Lte(got, 157, "checks %v is ⤠157", got)1402 fmt.Println(ok)1403 ok = t.Lte(got, 155, "checks %v is ⤠155", got)1404 fmt.Println(ok)1405 // Output:1406 // true1407 // true1408 // false1409}1410func ExampleT_Lte_string() {1411 t := td.NewT(&testing.T{})1412 got := "abc"1413 ok := t.Lte(got, "abc", `checks "%v" is ⤠"abc"`, got)1414 fmt.Println(ok)1415 ok = t.Lte(got, "abd", `checks "%v" is ⤠"abd"`, got)1416 fmt.Println(ok)1417 ok = t.Lte(got, "abb", `checks "%v" is ⤠"abb"`, got)1418 fmt.Println(ok)1419 // Output:1420 // true1421 // true1422 // false1423}1424func ExampleT_Map_map() {1425 t := td.NewT(&testing.T{})1426 got := map[string]int{"foo": 12, "bar": 42, "zip": 89}1427 ok := t.Map(got, map[string]int{"bar": 42}, td.MapEntries{"foo": td.Lt(15), "zip": td.Ignore()},1428 "checks map %v", got)1429 fmt.Println(ok)1430 ok = t.Map(got, map[string]int{}, td.MapEntries{"bar": 42, "foo": td.Lt(15), "zip": td.Ignore()},1431 "checks map %v", got)1432 fmt.Println(ok)1433 ok = t.Map(got, (map[string]int)(nil), td.MapEntries{"bar": 42, "foo": td.Lt(15), "zip": td.Ignore()},1434 "checks map %v", got)1435 fmt.Println(ok)1436 // Output:1437 // true1438 // true1439 // true1440}1441func ExampleT_Map_typedMap() {1442 t := td.NewT(&testing.T{})1443 type MyMap map[string]int1444 got := MyMap{"foo": 12, "bar": 42, "zip": 89}1445 ok := t.Map(got, MyMap{"bar": 42}, td.MapEntries{"foo": td.Lt(15), "zip": td.Ignore()},1446 "checks typed map %v", got)1447 fmt.Println(ok)1448 ok = t.Map(&got, &MyMap{"bar": 42}, td.MapEntries{"foo": td.Lt(15), "zip": td.Ignore()},1449 "checks pointer on typed map %v", got)1450 fmt.Println(ok)1451 ok = t.Map(&got, &MyMap{}, td.MapEntries{"bar": 42, "foo": td.Lt(15), "zip": td.Ignore()},1452 "checks pointer on typed map %v", got)1453 fmt.Println(ok)1454 ok = t.Map(&got, (*MyMap)(nil), td.MapEntries{"bar": 42, "foo": td.Lt(15), "zip": td.Ignore()},1455 "checks pointer on typed map %v", got)1456 fmt.Println(ok)1457 // Output:1458 // true1459 // true1460 // true1461 // true1462}1463func ExampleT_MapEach_map() {1464 t := td.NewT(&testing.T{})1465 got := map[string]int{"foo": 12, "bar": 42, "zip": 89}1466 ok := t.MapEach(got, td.Between(10, 90),1467 "checks each value of map %v is in [10 .. 90]", got)1468 fmt.Println(ok)1469 // Output:1470 // true1471}1472func ExampleT_MapEach_typedMap() {1473 t := td.NewT(&testing.T{})1474 type MyMap map[string]int1475 got := MyMap{"foo": 12, "bar": 42, "zip": 89}1476 ok := t.MapEach(got, td.Between(10, 90),1477 "checks each value of typed map %v is in [10 .. 90]", got)1478 fmt.Println(ok)1479 ok = t.MapEach(&got, td.Between(10, 90),1480 "checks each value of typed map pointer %v is in [10 .. 90]", got)1481 fmt.Println(ok)1482 // Output:1483 // true1484 // true1485}1486func ExampleT_N() {1487 t := td.NewT(&testing.T{})1488 got := 1.123451489 ok := t.N(got, 1.1234, 0.00006,1490 "checks %v = 1.1234 ± 0.00006", got)1491 fmt.Println(ok)1492 // Output:1493 // true1494}1495func ExampleT_NaN_float32() {1496 t := td.NewT(&testing.T{})1497 got := float32(math.NaN())1498 ok := t.NaN(got,1499 "checks %v is not-a-number", got)1500 fmt.Println("float32(math.NaN()) is float32 not-a-number:", ok)1501 got = 121502 ok = t.NaN(got,1503 "checks %v is not-a-number", got)1504 fmt.Println("float32(12) is float32 not-a-number:", ok)1505 // Output:1506 // float32(math.NaN()) is float32 not-a-number: true1507 // float32(12) is float32 not-a-number: false1508}1509func ExampleT_NaN_float64() {1510 t := td.NewT(&testing.T{})1511 got := math.NaN()1512 ok := t.NaN(got,1513 "checks %v is not-a-number", got)1514 fmt.Println("math.NaN() is not-a-number:", ok)1515 got = 121516 ok = t.NaN(got,1517 "checks %v is not-a-number", got)1518 fmt.Println("float64(12) is not-a-number:", ok)1519 // math.NaN() is not-a-number: true1520 // float64(12) is not-a-number: false1521}1522func ExampleT_Nil() {1523 t := td.NewT(&testing.T{})1524 var got fmt.Stringer // interface1525 // nil value can be compared directly with nil, no need of Nil() here1526 ok := t.Cmp(got, nil)1527 fmt.Println(ok)1528 // But it works with Nil() anyway1529 ok = t.Nil(got)1530 fmt.Println(ok)1531 got = (*bytes.Buffer)(nil)1532 // In the case of an interface containing a nil pointer, comparing1533 // with nil fails, as the interface is not nil1534 ok = t.Cmp(got, nil)1535 fmt.Println(ok)1536 // In this case Nil() succeed1537 ok = t.Nil(got)1538 fmt.Println(ok)1539 // Output:1540 // true1541 // true1542 // false1543 // true1544}1545func ExampleT_None() {1546 t := td.NewT(&testing.T{})1547 got := 181548 ok := t.None(got, []any{0, 10, 20, 30, td.Between(100, 199)},1549 "checks %v is non-null, and â 10, 20 & 30, and not in [100-199]", got)1550 fmt.Println(ok)1551 got = 201552 ok = t.None(got, []any{0, 10, 20, 30, td.Between(100, 199)},1553 "checks %v is non-null, and â 10, 20 & 30, and not in [100-199]", got)1554 fmt.Println(ok)1555 got = 1421556 ok = t.None(got, []any{0, 10, 20, 30, td.Between(100, 199)},1557 "checks %v is non-null, and â 10, 20 & 30, and not in [100-199]", got)1558 fmt.Println(ok)1559 prime := td.Flatten([]int{1, 2, 3, 5, 7, 11, 13})1560 even := td.Flatten([]int{2, 4, 6, 8, 10, 12, 14})1561 for _, got := range [...]int{9, 3, 8, 15} {1562 ok = t.None(got, []any{prime, even, td.Gt(14)},1563 "checks %v is not prime number, nor an even number and not > 14")1564 fmt.Printf("%d â %t\n", got, ok)1565 }1566 // Output:1567 // true1568 // false1569 // false1570 // 9 â true1571 // 3 â false1572 // 8 â false1573 // 15 â false1574}1575func ExampleT_Not() {1576 t := td.NewT(&testing.T{})1577 got := 421578 ok := t.Not(got, 0, "checks %v is non-null", got)1579 fmt.Println(ok)1580 ok = t.Not(got, td.Between(10, 30),1581 "checks %v is not in [10 .. 30]", got)1582 fmt.Println(ok)1583 got = 01584 ok = t.Not(got, 0, "checks %v is non-null", got)1585 fmt.Println(ok)1586 // Output:1587 // true1588 // true1589 // false1590}1591func ExampleT_NotAny() {1592 t := td.NewT(&testing.T{})1593 got := []int{4, 5, 9, 42}1594 ok := t.NotAny(got, []any{3, 6, 8, 41, 43},1595 "checks %v contains no item listed in NotAny()", got)1596 fmt.Println(ok)1597 ok = t.NotAny(got, []any{3, 6, 8, 42, 43},1598 "checks %v contains no item listed in NotAny()", got)1599 fmt.Println(ok)1600 // When expected is already a non-[]any slice, it cannot be1601 // flattened directly using notExpected... without copying it to a new1602 // []any slice, then use td.Flatten!1603 notExpected := []int{3, 6, 8, 41, 43}1604 ok = t.NotAny(got, []any{td.Flatten(notExpected)},1605 "checks %v contains no item listed in notExpected", got)1606 fmt.Println(ok)1607 // Output:1608 // true1609 // false1610 // true1611}1612func ExampleT_NotEmpty() {1613 t := td.NewT(&testing.T{})1614 ok := t.NotEmpty(nil) // fails, as nil is considered empty1615 fmt.Println(ok)1616 ok = t.NotEmpty("foobar")1617 fmt.Println(ok)1618 // Fails as 0 is a number, so not empty. Use NotZero() instead1619 ok = t.NotEmpty(0)1620 fmt.Println(ok)1621 ok = t.NotEmpty(map[string]int{"foobar": 42})1622 fmt.Println(ok)1623 ok = t.NotEmpty([]int{1})1624 fmt.Println(ok)1625 ok = t.NotEmpty([3]int{}) // succeeds, NotEmpty() is not NotZero()!1626 fmt.Println(ok)1627 // Output:1628 // false1629 // true1630 // false1631 // true1632 // true1633 // true1634}1635func ExampleT_NotEmpty_pointers() {1636 t := td.NewT(&testing.T{})1637 type MySlice []int1638 ok := t.NotEmpty(MySlice{12})1639 fmt.Println(ok)1640 ok = t.NotEmpty(&MySlice{12}) // Ptr() not needed1641 fmt.Println(ok)1642 l1 := &MySlice{12}1643 l2 := &l11644 l3 := &l21645 ok = t.NotEmpty(&l3)1646 fmt.Println(ok)1647 // Works the same for array, map, channel and string1648 // But not for others types as:1649 type MyStruct struct {1650 Value int1651 }1652 ok = t.NotEmpty(&MyStruct{}) // fails, use NotZero() instead1653 fmt.Println(ok)1654 // Output:1655 // true1656 // true1657 // true1658 // false1659}1660func ExampleT_NotNaN_float32() {1661 t := td.NewT(&testing.T{})1662 got := float32(math.NaN())1663 ok := t.NotNaN(got,1664 "checks %v is not-a-number", got)1665 fmt.Println("float32(math.NaN()) is NOT float32 not-a-number:", ok)1666 got = 121667 ok = t.NotNaN(got,1668 "checks %v is not-a-number", got)1669 fmt.Println("float32(12) is NOT float32 not-a-number:", ok)1670 // Output:1671 // float32(math.NaN()) is NOT float32 not-a-number: false1672 // float32(12) is NOT float32 not-a-number: true1673}1674func ExampleT_NotNaN_float64() {1675 t := td.NewT(&testing.T{})1676 got := math.NaN()1677 ok := t.NotNaN(got,1678 "checks %v is not-a-number", got)1679 fmt.Println("math.NaN() is not-a-number:", ok)1680 got = 121681 ok = t.NotNaN(got,1682 "checks %v is not-a-number", got)1683 fmt.Println("float64(12) is not-a-number:", ok)1684 // math.NaN() is NOT not-a-number: false1685 // float64(12) is NOT not-a-number: true1686}1687func ExampleT_NotNil() {1688 t := td.NewT(&testing.T{})1689 var got fmt.Stringer = &bytes.Buffer{}1690 // nil value can be compared directly with Not(nil), no need of NotNil() here1691 ok := t.Cmp(got, td.Not(nil))1692 fmt.Println(ok)1693 // But it works with NotNil() anyway1694 ok = t.NotNil(got)1695 fmt.Println(ok)1696 got = (*bytes.Buffer)(nil)1697 // In the case of an interface containing a nil pointer, comparing1698 // with Not(nil) succeeds, as the interface is not nil1699 ok = t.Cmp(got, td.Not(nil))1700 fmt.Println(ok)1701 // In this case NotNil() fails1702 ok = t.NotNil(got)1703 fmt.Println(ok)1704 // Output:1705 // true1706 // true1707 // true1708 // false1709}1710func ExampleT_NotZero() {1711 t := td.NewT(&testing.T{})1712 ok := t.NotZero(0) // fails1713 fmt.Println(ok)1714 ok = t.NotZero(float64(0)) // fails1715 fmt.Println(ok)1716 ok = t.NotZero(12)1717 fmt.Println(ok)1718 ok = t.NotZero((map[string]int)(nil)) // fails, as nil1719 fmt.Println(ok)1720 ok = t.NotZero(map[string]int{}) // succeeds, as not nil1721 fmt.Println(ok)1722 ok = t.NotZero(([]int)(nil)) // fails, as nil1723 fmt.Println(ok)1724 ok = t.NotZero([]int{}) // succeeds, as not nil1725 fmt.Println(ok)1726 ok = t.NotZero([3]int{}) // fails1727 fmt.Println(ok)1728 ok = t.NotZero([3]int{0, 1}) // succeeds, DATA[1] is not 01729 fmt.Println(ok)1730 ok = t.NotZero(bytes.Buffer{}) // fails1731 fmt.Println(ok)1732 ok = t.NotZero(&bytes.Buffer{}) // succeeds, as pointer not nil1733 fmt.Println(ok)1734 ok = t.Cmp(&bytes.Buffer{}, td.Ptr(td.NotZero())) // fails as deref by Ptr()1735 fmt.Println(ok)1736 // Output:1737 // false1738 // false1739 // true1740 // false1741 // true1742 // false1743 // true1744 // false1745 // true1746 // false1747 // true1748 // false1749}1750func ExampleT_PPtr() {1751 t := td.NewT(&testing.T{})1752 num := 121753 got := &num1754 ok := t.PPtr(&got, 12)1755 fmt.Println(ok)1756 ok = t.PPtr(&got, td.Between(4, 15))1757 fmt.Println(ok)1758 // Output:1759 // true1760 // true1761}1762func ExampleT_Ptr() {1763 t := td.NewT(&testing.T{})1764 got := 121765 ok := t.Ptr(&got, 12)1766 fmt.Println(ok)1767 ok = t.Ptr(&got, td.Between(4, 15))1768 fmt.Println(ok)1769 // Output:1770 // true1771 // true1772}1773func ExampleT_Re() {1774 t := td.NewT(&testing.T{})1775 got := "foo bar"1776 ok := t.Re(got, "(zip|bar)$", nil, "checks value %s", got)1777 fmt.Println(ok)1778 got = "bar foo"1779 ok = t.Re(got, "(zip|bar)$", nil, "checks value %s", got)1780 fmt.Println(ok)1781 // Output:1782 // true1783 // false1784}1785func ExampleT_Re_stringer() {1786 t := td.NewT(&testing.T{})1787 // bytes.Buffer implements fmt.Stringer1788 got := bytes.NewBufferString("foo bar")1789 ok := t.Re(got, "(zip|bar)$", nil, "checks value %s", got)1790 fmt.Println(ok)1791 // Output:1792 // true1793}1794func ExampleT_Re_error() {1795 t := td.NewT(&testing.T{})1796 got := errors.New("foo bar")1797 ok := t.Re(got, "(zip|bar)$", nil, "checks value %s", got)1798 fmt.Println(ok)1799 // Output:1800 // true1801}1802func ExampleT_Re_capture() {1803 t := td.NewT(&testing.T{})1804 got := "foo bar biz"1805 ok := t.Re(got, `^(\w+) (\w+) (\w+)$`, td.Set("biz", "foo", "bar"),1806 "checks value %s", got)1807 fmt.Println(ok)1808 got = "foo bar! biz"1809 ok = t.Re(got, `^(\w+) (\w+) (\w+)$`, td.Set("biz", "foo", "bar"),1810 "checks value %s", got)1811 fmt.Println(ok)1812 // Output:1813 // true1814 // false1815}1816func ExampleT_Re_compiled() {1817 t := td.NewT(&testing.T{})1818 expected := regexp.MustCompile("(zip|bar)$")1819 got := "foo bar"1820 ok := t.Re(got, expected, nil, "checks value %s", got)1821 fmt.Println(ok)1822 got = "bar foo"1823 ok = t.Re(got, expected, nil, "checks value %s", got)1824 fmt.Println(ok)1825 // Output:1826 // true1827 // false1828}1829func ExampleT_Re_compiledStringer() {1830 t := td.NewT(&testing.T{})1831 expected := regexp.MustCompile("(zip|bar)$")1832 // bytes.Buffer implements fmt.Stringer1833 got := bytes.NewBufferString("foo bar")1834 ok := t.Re(got, expected, nil, "checks value %s", got)1835 fmt.Println(ok)1836 // Output:1837 // true1838}1839func ExampleT_Re_compiledError() {1840 t := td.NewT(&testing.T{})1841 expected := regexp.MustCompile("(zip|bar)$")1842 got := errors.New("foo bar")1843 ok := t.Re(got, expected, nil, "checks value %s", got)1844 fmt.Println(ok)1845 // Output:1846 // true1847}1848func ExampleT_Re_compiledCapture() {1849 t := td.NewT(&testing.T{})1850 expected := regexp.MustCompile(`^(\w+) (\w+) (\w+)$`)1851 got := "foo bar biz"1852 ok := t.Re(got, expected, td.Set("biz", "foo", "bar"),1853 "checks value %s", got)1854 fmt.Println(ok)1855 got = "foo bar! biz"1856 ok = t.Re(got, expected, td.Set("biz", "foo", "bar"),1857 "checks value %s", got)1858 fmt.Println(ok)1859 // Output:1860 // true1861 // false1862}1863func ExampleT_ReAll_capture() {1864 t := td.NewT(&testing.T{})1865 got := "foo bar biz"1866 ok := t.ReAll(got, `(\w+)`, td.Set("biz", "foo", "bar"),1867 "checks value %s", got)1868 fmt.Println(ok)1869 // Matches, but all catured groups do not match Set1870 got = "foo BAR biz"1871 ok = t.ReAll(got, `(\w+)`, td.Set("biz", "foo", "bar"),1872 "checks value %s", got)1873 fmt.Println(ok)1874 // Output:1875 // true1876 // false1877}1878func ExampleT_ReAll_captureComplex() {1879 t := td.NewT(&testing.T{})1880 got := "11 45 23 56 85 96"1881 ok := t.ReAll(got, `(\d+)`, td.ArrayEach(td.Code(func(num string) bool {1882 n, err := strconv.Atoi(num)1883 return err == nil && n > 10 && n < 1001884 })),1885 "checks value %s", got)1886 fmt.Println(ok)1887 // Matches, but 11 is not greater than 201888 ok = t.ReAll(got, `(\d+)`, td.ArrayEach(td.Code(func(num string) bool {1889 n, err := strconv.Atoi(num)1890 return err == nil && n > 20 && n < 1001891 })),1892 "checks value %s", got)1893 fmt.Println(ok)1894 // Output:1895 // true1896 // false1897}1898func ExampleT_ReAll_compiledCapture() {1899 t := td.NewT(&testing.T{})1900 expected := regexp.MustCompile(`(\w+)`)1901 got := "foo bar biz"1902 ok := t.ReAll(got, expected, td.Set("biz", "foo", "bar"),1903 "checks value %s", got)1904 fmt.Println(ok)1905 // Matches, but all catured groups do not match Set1906 got = "foo BAR biz"1907 ok = t.ReAll(got, expected, td.Set("biz", "foo", "bar"),1908 "checks value %s", got)1909 fmt.Println(ok)1910 // Output:1911 // true1912 // false1913}1914func ExampleT_ReAll_compiledCaptureComplex() {1915 t := td.NewT(&testing.T{})1916 expected := regexp.MustCompile(`(\d+)`)1917 got := "11 45 23 56 85 96"1918 ok := t.ReAll(got, expected, td.ArrayEach(td.Code(func(num string) bool {1919 n, err := strconv.Atoi(num)1920 return err == nil && n > 10 && n < 1001921 })),1922 "checks value %s", got)1923 fmt.Println(ok)1924 // Matches, but 11 is not greater than 201925 ok = t.ReAll(got, expected, td.ArrayEach(td.Code(func(num string) bool {1926 n, err := strconv.Atoi(num)1927 return err == nil && n > 20 && n < 1001928 })),1929 "checks value %s", got)1930 fmt.Println(ok)1931 // Output:1932 // true1933 // false1934}1935func ExampleT_Recv_basic() {1936 t := td.NewT(&testing.T{})1937 got := make(chan int, 3)1938 ok := t.Recv(got, td.RecvNothing, 0)1939 fmt.Println("nothing to receive:", ok)1940 got <- 11941 got <- 21942 got <- 31943 close(got)1944 ok = t.Recv(got, 1, 0)1945 fmt.Println("1st receive is 1:", ok)1946 ok = t.Cmp(got, td.All(1947 td.Recv(2),1948 td.Recv(td.Between(3, 4)),1949 td.Recv(td.RecvClosed),1950 ))1951 fmt.Println("next receives are 2, 3 then closed:", ok)1952 ok = t.Recv(got, td.RecvNothing, 0)1953 fmt.Println("nothing to receive:", ok)1954 // Output:1955 // nothing to receive: true1956 // 1st receive is 1: true1957 // next receives are 2, 3 then closed: true1958 // nothing to receive: false1959}1960func ExampleT_Recv_channelPointer() {1961 t := td.NewT(&testing.T{})1962 got := make(chan int, 3)1963 ok := t.Recv(got, td.RecvNothing, 0)1964 fmt.Println("nothing to receive:", ok)1965 got <- 11966 got <- 21967 got <- 31968 close(got)1969 ok = t.Recv(&got, 1, 0)1970 fmt.Println("1st receive is 1:", ok)1971 ok = t.Cmp(&got, td.All(1972 td.Recv(2),1973 td.Recv(td.Between(3, 4)),1974 td.Recv(td.RecvClosed),1975 ))1976 fmt.Println("next receives are 2, 3 then closed:", ok)1977 ok = t.Recv(got, td.RecvNothing, 0)1978 fmt.Println("nothing to receive:", ok)1979 // Output:1980 // nothing to receive: true1981 // 1st receive is 1: true1982 // next receives are 2, 3 then closed: true1983 // nothing to receive: false1984}1985func ExampleT_Recv_withTimeout() {1986 t := td.NewT(&testing.T{})1987 got := make(chan int, 1)1988 tick := make(chan struct{})1989 go func() {1990 // â 1991 <-tick1992 time.Sleep(100 * time.Millisecond)1993 got <- 01994 // â¡1995 <-tick1996 time.Sleep(100 * time.Millisecond)1997 got <- 11998 // â¢1999 <-tick2000 time.Sleep(100 * time.Millisecond)2001 close(got)2002 }()2003 t.Recv(got, td.RecvNothing, 0)2004 // â 2005 tick <- struct{}{}2006 ok := t.Recv(got, td.RecvNothing, 0)2007 fmt.Println("â RecvNothing:", ok)2008 ok = t.Recv(got, 0, 150*time.Millisecond)2009 fmt.Println("â receive 0 w/150ms timeout:", ok)2010 ok = t.Recv(got, td.RecvNothing, 0)2011 fmt.Println("â RecvNothing:", ok)2012 // â¡2013 tick <- struct{}{}2014 ok = t.Recv(got, td.RecvNothing, 0)2015 fmt.Println("â¡ RecvNothing:", ok)2016 ok = t.Recv(got, 1, 150*time.Millisecond)2017 fmt.Println("â¡ receive 1 w/150ms timeout:", ok)2018 ok = t.Recv(got, td.RecvNothing, 0)2019 fmt.Println("â¡ RecvNothing:", ok)2020 // â¢2021 tick <- struct{}{}2022 ok = t.Recv(got, td.RecvNothing, 0)2023 fmt.Println("⢠RecvNothing:", ok)2024 ok = t.Recv(got, td.RecvClosed, 150*time.Millisecond)2025 fmt.Println("⢠check closed w/150ms timeout:", ok)2026 // Output:2027 // â RecvNothing: true2028 // â receive 0 w/150ms timeout: true2029 // â RecvNothing: true2030 // â¡ RecvNothing: true2031 // â¡ receive 1 w/150ms timeout: true2032 // â¡ RecvNothing: true2033 // ⢠RecvNothing: true2034 // ⢠check closed w/150ms timeout: true2035}2036func ExampleT_Recv_nilChannel() {2037 t := td.NewT(&testing.T{})2038 var ch chan int2039 ok := t.Recv(ch, td.RecvNothing, 0)2040 fmt.Println("nothing to receive from nil channel:", ok)2041 ok = t.Recv(ch, 42, 0)2042 fmt.Println("something to receive from nil channel:", ok)2043 ok = t.Recv(ch, td.RecvClosed, 0)2044 fmt.Println("is a nil channel closed:", ok)2045 // Output:2046 // nothing to receive from nil channel: true2047 // something to receive from nil channel: false2048 // is a nil channel closed: false2049}2050func ExampleT_Set() {2051 t := td.NewT(&testing.T{})2052 got := []int{1, 3, 5, 8, 8, 1, 2}2053 // Matches as all items are present, ignoring duplicates2054 ok := t.Set(got, []any{1, 2, 3, 5, 8},2055 "checks all items are present, in any order")2056 fmt.Println(ok)2057 // Duplicates are ignored in a Set2058 ok = t.Set(got, []any{1, 2, 2, 2, 2, 2, 3, 5, 8},2059 "checks all items are present, in any order")2060 fmt.Println(ok)2061 // Tries its best to not raise an error when a value can be matched2062 // by several Set entries2063 ok = t.Set(got, []any{td.Between(1, 4), 3, td.Between(2, 10)},2064 "checks all items are present, in any order")2065 fmt.Println(ok)2066 // When expected is already a non-[]any slice, it cannot be2067 // flattened directly using expected... without copying it to a new2068 // []any slice, then use td.Flatten!2069 expected := []int{1, 2, 3, 5, 8}2070 ok = t.Set(got, []any{td.Flatten(expected)},2071 "checks all expected items are present, in any order")2072 fmt.Println(ok)2073 // Output:2074 // true2075 // true2076 // true2077 // true2078}2079func ExampleT_Shallow() {2080 t := td.NewT(&testing.T{})2081 type MyStruct struct {2082 Value int2083 }2084 data := MyStruct{Value: 12}2085 got := &data2086 ok := t.Shallow(got, &data,2087 "checks pointers only, not contents")2088 fmt.Println(ok)2089 // Same contents, but not same pointer2090 ok = t.Shallow(got, &MyStruct{Value: 12},2091 "checks pointers only, not contents")2092 fmt.Println(ok)2093 // Output:2094 // true2095 // false2096}2097func ExampleT_Shallow_slice() {2098 t := td.NewT(&testing.T{})2099 back := []int{1, 2, 3, 1, 2, 3}2100 a := back[:3]2101 b := back[3:]2102 ok := t.Shallow(a, back)2103 fmt.Println("are â but share the same area:", ok)2104 ok = t.Shallow(b, back)2105 fmt.Println("are = but do not point to same area:", ok)2106 // Output:2107 // are â but share the same area: true2108 // are = but do not point to same area: false2109}2110func ExampleT_Shallow_string() {2111 t := td.NewT(&testing.T{})2112 back := "foobarfoobar"2113 a := back[:6]2114 b := back[6:]2115 ok := t.Shallow(a, back)2116 fmt.Println("are â but share the same area:", ok)2117 ok = t.Shallow(b, a)2118 fmt.Println("are = but do not point to same area:", ok)2119 // Output:2120 // are â but share the same area: true2121 // are = but do not point to same area: false2122}2123func ExampleT_Slice_slice() {2124 t := td.NewT(&testing.T{})2125 got := []int{42, 58, 26}2126 ok := t.Slice(got, []int{42}, td.ArrayEntries{1: 58, 2: td.Ignore()},2127 "checks slice %v", got)2128 fmt.Println(ok)2129 ok = t.Slice(got, []int{}, td.ArrayEntries{0: 42, 1: 58, 2: td.Ignore()},2130 "checks slice %v", got)2131 fmt.Println(ok)2132 ok = t.Slice(got, ([]int)(nil), td.ArrayEntries{0: 42, 1: 58, 2: td.Ignore()},2133 "checks slice %v", got)2134 fmt.Println(ok)2135 // Output:2136 // true2137 // true2138 // true2139}2140func ExampleT_Slice_typedSlice() {2141 t := td.NewT(&testing.T{})2142 type MySlice []int2143 got := MySlice{42, 58, 26}2144 ok := t.Slice(got, MySlice{42}, td.ArrayEntries{1: 58, 2: td.Ignore()},2145 "checks typed slice %v", got)2146 fmt.Println(ok)2147 ok = t.Slice(&got, &MySlice{42}, td.ArrayEntries{1: 58, 2: td.Ignore()},2148 "checks pointer on typed slice %v", got)2149 fmt.Println(ok)2150 ok = t.Slice(&got, &MySlice{}, td.ArrayEntries{0: 42, 1: 58, 2: td.Ignore()},2151 "checks pointer on typed slice %v", got)2152 fmt.Println(ok)2153 ok = t.Slice(&got, (*MySlice)(nil), td.ArrayEntries{0: 42, 1: 58, 2: td.Ignore()},2154 "checks pointer on typed slice %v", got)2155 fmt.Println(ok)2156 // Output:2157 // true2158 // true2159 // true2160 // true2161}2162func ExampleT_Smuggle_convert() {2163 t := td.NewT(&testing.T{})2164 got := int64(123)2165 ok := t.Smuggle(got, func(n int64) int { return int(n) }, 123,2166 "checks int64 got against an int value")2167 fmt.Println(ok)2168 ok = t.Smuggle("123", func(numStr string) (int, bool) {2169 n, err := strconv.Atoi(numStr)2170 return n, err == nil2171 }, td.Between(120, 130),2172 "checks that number in %#v is in [120 .. 130]")2173 fmt.Println(ok)2174 ok = t.Smuggle("123", func(numStr string) (int, bool, string) {2175 n, err := strconv.Atoi(numStr)2176 if err != nil {2177 return 0, false, "string must contain a number"2178 }2179 return n, true, ""2180 }, td.Between(120, 130),2181 "checks that number in %#v is in [120 .. 130]")2182 fmt.Println(ok)2183 ok = t.Smuggle("123", func(numStr string) (int, error) { //nolint: gocritic2184 return strconv.Atoi(numStr)2185 }, td.Between(120, 130),2186 "checks that number in %#v is in [120 .. 130]")2187 fmt.Println(ok)2188 // Short version :)2189 ok = t.Smuggle("123", strconv.Atoi, td.Between(120, 130),2190 "checks that number in %#v is in [120 .. 130]")2191 fmt.Println(ok)2192 // Output:2193 // true2194 // true2195 // true2196 // true2197 // true2198}2199func ExampleT_Smuggle_lax() {2200 t := td.NewT(&testing.T{})2201 // got is an int16 and Smuggle func input is an int64: it is OK2202 got := int(123)2203 ok := t.Smuggle(got, func(n int64) uint32 { return uint32(n) }, uint32(123))2204 fmt.Println("got int16(123) â smuggle via int64 â uint32(123):", ok)2205 // Output:2206 // got int16(123) â smuggle via int64 â uint32(123): true2207}2208func ExampleT_Smuggle_auto_unmarshal() {2209 t := td.NewT(&testing.T{})2210 // Automatically json.Unmarshal to compare2211 got := []byte(`{"a":1,"b":2}`)2212 ok := t.Smuggle(got, func(b json.RawMessage) (r map[string]int, err error) {2213 err = json.Unmarshal(b, &r)2214 return2215 }, map[string]int{2216 "a": 1,2217 "b": 2,2218 })2219 fmt.Println("JSON contents is OK:", ok)2220 // Output:2221 // JSON contents is OK: true2222}2223func ExampleT_Smuggle_cast() {2224 t := td.NewT(&testing.T{})2225 // A string containing JSON2226 got := `{ "foo": 123 }`2227 // Automatically cast a string to a json.RawMessage so td.JSON can operate2228 ok := t.Smuggle(got, json.RawMessage{}, td.JSON(`{"foo":123}`))2229 fmt.Println("JSON contents in string is OK:", ok)2230 // Automatically read from io.Reader to a json.RawMessage2231 ok = t.Smuggle(bytes.NewReader([]byte(got)), json.RawMessage{}, td.JSON(`{"foo":123}`))2232 fmt.Println("JSON contents just read is OK:", ok)2233 // Output:2234 // JSON contents in string is OK: true2235 // JSON contents just read is OK: true2236}2237func ExampleT_Smuggle_complex() {2238 t := td.NewT(&testing.T{})2239 // No end date but a start date and a duration2240 type StartDuration struct {2241 StartDate time.Time2242 Duration time.Duration2243 }2244 // Checks that end date is between 17th and 19th February both at 0h2245 // for each of these durations in hours2246 for _, duration := range []time.Duration{48 * time.Hour, 72 * time.Hour, 96 * time.Hour} {2247 got := StartDuration{2248 StartDate: time.Date(2018, time.February, 14, 12, 13, 14, 0, time.UTC),2249 Duration: duration,2250 }2251 // Simplest way, but in case of Between() failure, error will be bound2252 // to DATA<smuggled>, not very clear...2253 ok := t.Smuggle(got, func(sd StartDuration) time.Time {2254 return sd.StartDate.Add(sd.Duration)2255 }, td.Between(2256 time.Date(2018, time.February, 17, 0, 0, 0, 0, time.UTC),2257 time.Date(2018, time.February, 19, 0, 0, 0, 0, time.UTC)))2258 fmt.Println(ok)2259 // Name the computed value "ComputedEndDate" to render a Between() failure2260 // more understandable, so error will be bound to DATA.ComputedEndDate2261 ok = t.Smuggle(got, func(sd StartDuration) td.SmuggledGot {2262 return td.SmuggledGot{2263 Name: "ComputedEndDate",2264 Got: sd.StartDate.Add(sd.Duration),2265 }2266 }, td.Between(2267 time.Date(2018, time.February, 17, 0, 0, 0, 0, time.UTC),2268 time.Date(2018, time.February, 19, 0, 0, 0, 0, time.UTC)))2269 fmt.Println(ok)2270 }2271 // Output:2272 // false2273 // false2274 // true2275 // true2276 // true2277 // true2278}2279func ExampleT_Smuggle_interface() {2280 t := td.NewT(&testing.T{})2281 gotTime, err := time.Parse(time.RFC3339, "2018-05-23T12:13:14Z")2282 if err != nil {2283 t.Fatal(err)2284 }2285 // Do not check the struct itself, but its stringified form2286 ok := t.Smuggle(gotTime, func(s fmt.Stringer) string {2287 return s.String()2288 }, "2018-05-23 12:13:14 +0000 UTC")2289 fmt.Println("stringified time.Time OK:", ok)2290 // If got does not implement the fmt.Stringer interface, it fails2291 // without calling the Smuggle func2292 type MyTime time.Time2293 ok = t.Smuggle(MyTime(gotTime), func(s fmt.Stringer) string {2294 fmt.Println("Smuggle func called!")2295 return s.String()2296 }, "2018-05-23 12:13:14 +0000 UTC")2297 fmt.Println("stringified MyTime OK:", ok)2298 // Output:2299 // stringified time.Time OK: true2300 // stringified MyTime OK: false2301}2302func ExampleT_Smuggle_field_path() {2303 t := td.NewT(&testing.T{})2304 type Body struct {2305 Name string2306 Value any2307 }2308 type Request struct {2309 Body *Body2310 }2311 type Transaction struct {2312 Request2313 }2314 type ValueNum struct {2315 Num int2316 }2317 got := &Transaction{2318 Request: Request{2319 Body: &Body{2320 Name: "test",2321 Value: &ValueNum{Num: 123},2322 },2323 },2324 }2325 // Want to check whether Num is between 100 and 200?2326 ok := t.Smuggle(got, func(t *Transaction) (int, error) {2327 if t.Request.Body == nil ||2328 t.Request.Body.Value == nil {2329 return 0, errors.New("Request.Body or Request.Body.Value is nil")2330 }2331 if v, ok := t.Request.Body.Value.(*ValueNum); ok && v != nil {2332 return v.Num, nil2333 }2334 return 0, errors.New("Request.Body.Value isn't *ValueNum or nil")2335 }, td.Between(100, 200))2336 fmt.Println("check Num by hand:", ok)2337 // Same, but automagically generated...2338 ok = t.Smuggle(got, "Request.Body.Value.Num", td.Between(100, 200))2339 fmt.Println("check Num using a fields-path:", ok)2340 // And as Request is an anonymous field, can be simplified further2341 // as it can be omitted2342 ok = t.Smuggle(got, "Body.Value.Num", td.Between(100, 200))2343 fmt.Println("check Num using an other fields-path:", ok)2344 // Note that maps and array/slices are supported2345 got.Request.Body.Value = map[string]any{2346 "foo": []any{2347 3: map[int]string{666: "bar"},2348 },2349 }2350 ok = t.Smuggle(got, "Body.Value[foo][3][666]", "bar")2351 fmt.Println("check fields-path including maps/slices:", ok)2352 // Output:2353 // check Num by hand: true2354 // check Num using a fields-path: true2355 // check Num using an other fields-path: true2356 // check fields-path including maps/slices: true2357}2358func ExampleT_SStruct() {2359 t := td.NewT(&testing.T{})2360 type Person struct {2361 Name string2362 Age int2363 NumChildren int2364 }2365 got := Person{2366 Name: "Foobar",2367 Age: 42,2368 NumChildren: 0,2369 }2370 // NumChildren is not listed in expected fields so it must be zero2371 ok := t.SStruct(got, Person{Name: "Foobar"}, td.StructFields{2372 "Age": td.Between(40, 50),2373 },2374 "checks %v is the right Person")2375 fmt.Println("Foobar is between 40 & 50:", ok)2376 // Model can be empty2377 got.NumChildren = 32378 ok = t.SStruct(got, Person{}, td.StructFields{2379 "Name": "Foobar",2380 "Age": td.Between(40, 50),2381 "NumChildren": td.Not(0),2382 },2383 "checks %v is the right Person")2384 fmt.Println("Foobar has some children:", ok)2385 // Works with pointers too2386 ok = t.SStruct(&got, &Person{}, td.StructFields{2387 "Name": "Foobar",2388 "Age": td.Between(40, 50),2389 "NumChildren": td.Not(0),2390 },2391 "checks %v is the right Person")2392 fmt.Println("Foobar has some children (using pointer):", ok)2393 // Model does not need to be instanciated2394 ok = t.SStruct(&got, (*Person)(nil), td.StructFields{2395 "Name": "Foobar",2396 "Age": td.Between(40, 50),2397 "NumChildren": td.Not(0),2398 },2399 "checks %v is the right Person")2400 fmt.Println("Foobar has some children (using nil model):", ok)2401 // Output:2402 // Foobar is between 40 & 50: true2403 // Foobar has some children: true2404 // Foobar has some children (using pointer): true2405 // Foobar has some children (using nil model): true2406}2407func ExampleT_SStruct_overwrite_model() {2408 t := td.NewT(&testing.T{})2409 type Person struct {2410 Name string2411 Age int2412 NumChildren int2413 }2414 got := Person{2415 Name: "Foobar",2416 Age: 42,2417 NumChildren: 3,2418 }2419 ok := t.SStruct(got, Person{2420 Name: "Foobar",2421 Age: 53,2422 }, td.StructFields{2423 ">Age": td.Between(40, 50), // ">" to overwrite Age:53 in model2424 "NumChildren": td.Gt(2),2425 },2426 "checks %v is the right Person")2427 fmt.Println("Foobar is between 40 & 50:", ok)2428 ok = t.SStruct(got, Person{2429 Name: "Foobar",2430 Age: 53,2431 }, td.StructFields{2432 "> Age": td.Between(40, 50), // same, ">" can be followed by spaces2433 "NumChildren": td.Gt(2),2434 },2435 "checks %v is the right Person")2436 fmt.Println("Foobar is between 40 & 50:", ok)2437 // Output:2438 // Foobar is between 40 & 50: true2439 // Foobar is between 40 & 50: true2440}2441func ExampleT_SStruct_patterns() {2442 t := td.NewT(&testing.T{})2443 type Person struct {2444 Firstname string2445 Lastname string2446 Surname string2447 Nickname string2448 CreatedAt time.Time2449 UpdatedAt time.Time2450 DeletedAt *time.Time2451 id int642452 secret string2453 }2454 now := time.Now()2455 got := Person{2456 Firstname: "Maxime",2457 Lastname: "Foo",2458 Surname: "Max",2459 Nickname: "max",2460 CreatedAt: now,2461 UpdatedAt: now,2462 DeletedAt: nil, // not deleted yet2463 id: 2345,2464 secret: "5ecr3T",2465 }2466 ok := t.SStruct(got, Person{Lastname: "Foo"}, td.StructFields{2467 `DeletedAt`: nil,2468 `= *name`: td.Re(`^(?i)max`), // shell pattern, matches all names except Lastname as in model2469 `=~ At\z`: td.Lte(time.Now()), // regexp, matches CreatedAt & UpdatedAt2470 `! [A-Z]*`: td.Ignore(), // private fields2471 },2472 "mix shell & regexp patterns")2473 fmt.Println("Patterns match only remaining fields:", ok)2474 ok = t.SStruct(got, Person{Lastname: "Foo"}, td.StructFields{2475 `DeletedAt`: nil,2476 `1 = *name`: td.Re(`^(?i)max`), // shell pattern, matches all names except Lastname as in model2477 `2 =~ At\z`: td.Lte(time.Now()), // regexp, matches CreatedAt & UpdatedAt2478 `3 !~ ^[A-Z]`: td.Ignore(), // private fields2479 },2480 "ordered patterns")2481 fmt.Println("Ordered patterns match only remaining fields:", ok)2482 // Output:2483 // Patterns match only remaining fields: true2484 // Ordered patterns match only remaining fields: true2485}2486func ExampleT_String() {2487 t := td.NewT(&testing.T{})2488 got := "foobar"2489 ok := t.String(got, "foobar", "checks %s", got)2490 fmt.Println("using string:", ok)2491 ok = t.Cmp([]byte(got), td.String("foobar"), "checks %s", got)2492 fmt.Println("using []byte:", ok)2493 // Output:2494 // using string: true2495 // using []byte: true2496}2497func ExampleT_String_stringer() {2498 t := td.NewT(&testing.T{})2499 // bytes.Buffer implements fmt.Stringer2500 got := bytes.NewBufferString("foobar")2501 ok := t.String(got, "foobar", "checks %s", got)2502 fmt.Println(ok)2503 // Output:2504 // true2505}2506func ExampleT_String_error() {2507 t := td.NewT(&testing.T{})2508 got := errors.New("foobar")2509 ok := t.String(got, "foobar", "checks %s", got)2510 fmt.Println(ok)2511 // Output:2512 // true2513}2514func ExampleT_Struct() {2515 t := td.NewT(&testing.T{})2516 type Person struct {2517 Name string2518 Age int2519 NumChildren int2520 }2521 got := Person{2522 Name: "Foobar",2523 Age: 42,2524 NumChildren: 3,2525 }2526 // As NumChildren is zero in Struct() call, it is not checked2527 ok := t.Struct(got, Person{Name: "Foobar"}, td.StructFields{2528 "Age": td.Between(40, 50),2529 },2530 "checks %v is the right Person")2531 fmt.Println("Foobar is between 40 & 50:", ok)2532 // Model can be empty2533 ok = t.Struct(got, Person{}, td.StructFields{2534 "Name": "Foobar",2535 "Age": td.Between(40, 50),2536 "NumChildren": td.Not(0),2537 },2538 "checks %v is the right Person")2539 fmt.Println("Foobar has some children:", ok)2540 // Works with pointers too2541 ok = t.Struct(&got, &Person{}, td.StructFields{2542 "Name": "Foobar",2543 "Age": td.Between(40, 50),2544 "NumChildren": td.Not(0),2545 },2546 "checks %v is the right Person")2547 fmt.Println("Foobar has some children (using pointer):", ok)2548 // Model does not need to be instanciated2549 ok = t.Struct(&got, (*Person)(nil), td.StructFields{2550 "Name": "Foobar",2551 "Age": td.Between(40, 50),2552 "NumChildren": td.Not(0),2553 },2554 "checks %v is the right Person")2555 fmt.Println("Foobar has some children (using nil model):", ok)2556 // Output:2557 // Foobar is between 40 & 50: true2558 // Foobar has some children: true2559 // Foobar has some children (using pointer): true2560 // Foobar has some children (using nil model): true2561}2562func ExampleT_Struct_overwrite_model() {2563 t := td.NewT(&testing.T{})2564 type Person struct {2565 Name string2566 Age int2567 NumChildren int2568 }2569 got := Person{2570 Name: "Foobar",2571 Age: 42,2572 NumChildren: 3,2573 }2574 ok := t.Struct(got, Person{2575 Name: "Foobar",2576 Age: 53,2577 }, td.StructFields{2578 ">Age": td.Between(40, 50), // ">" to overwrite Age:53 in model2579 "NumChildren": td.Gt(2),2580 },2581 "checks %v is the right Person")2582 fmt.Println("Foobar is between 40 & 50:", ok)2583 ok = t.Struct(got, Person{2584 Name: "Foobar",2585 Age: 53,2586 }, td.StructFields{2587 "> Age": td.Between(40, 50), // same, ">" can be followed by spaces2588 "NumChildren": td.Gt(2),2589 },2590 "checks %v is the right Person")2591 fmt.Println("Foobar is between 40 & 50:", ok)2592 // Output:2593 // Foobar is between 40 & 50: true2594 // Foobar is between 40 & 50: true2595}2596func ExampleT_Struct_patterns() {2597 t := td.NewT(&testing.T{})2598 type Person struct {2599 Firstname string2600 Lastname string2601 Surname string2602 Nickname string2603 CreatedAt time.Time2604 UpdatedAt time.Time2605 DeletedAt *time.Time2606 }2607 now := time.Now()2608 got := Person{2609 Firstname: "Maxime",2610 Lastname: "Foo",2611 Surname: "Max",2612 Nickname: "max",2613 CreatedAt: now,2614 UpdatedAt: now,2615 DeletedAt: nil, // not deleted yet2616 }2617 ok := t.Struct(got, Person{Lastname: "Foo"}, td.StructFields{2618 `DeletedAt`: nil,2619 `= *name`: td.Re(`^(?i)max`), // shell pattern, matches all names except Lastname as in model2620 `=~ At\z`: td.Lte(time.Now()), // regexp, matches CreatedAt & UpdatedAt2621 },2622 "mix shell & regexp patterns")2623 fmt.Println("Patterns match only remaining fields:", ok)2624 ok = t.Struct(got, Person{Lastname: "Foo"}, td.StructFields{2625 `DeletedAt`: nil,2626 `1 = *name`: td.Re(`^(?i)max`), // shell pattern, matches all names except Lastname as in model2627 `2 =~ At\z`: td.Lte(time.Now()), // regexp, matches CreatedAt & UpdatedAt2628 },2629 "ordered patterns")2630 fmt.Println("Ordered patterns match only remaining fields:", ok)2631 // Output:2632 // Patterns match only remaining fields: true2633 // Ordered patterns match only remaining fields: true2634}2635func ExampleT_SubBagOf() {2636 t := td.NewT(&testing.T{})2637 got := []int{1, 3, 5, 8, 8, 1, 2}2638 ok := t.SubBagOf(got, []any{0, 0, 1, 1, 2, 2, 3, 3, 5, 5, 8, 8, 9, 9},2639 "checks at least all items are present, in any order")2640 fmt.Println(ok)2641 // got contains one 8 too many2642 ok = t.SubBagOf(got, []any{0, 0, 1, 1, 2, 2, 3, 3, 5, 5, 8, 9, 9},2643 "checks at least all items are present, in any order")2644 fmt.Println(ok)2645 got = []int{1, 3, 5, 2}2646 ok = t.SubBagOf(got, []any{td.Between(0, 3), td.Between(0, 3), td.Between(0, 3), td.Between(0, 3), td.Gt(4), td.Gt(4)},2647 "checks at least all items match, in any order with TestDeep operators")2648 fmt.Println(ok)2649 // When expected is already a non-[]any slice, it cannot be2650 // flattened directly using expected... without copying it to a new2651 // []any slice, then use td.Flatten!2652 expected := []int{1, 2, 3, 5, 9, 8}2653 ok = t.SubBagOf(got, []any{td.Flatten(expected)},2654 "checks at least all expected items are present, in any order")2655 fmt.Println(ok)2656 // Output:2657 // true2658 // false2659 // true2660 // true2661}2662func ExampleT_SubJSONOf_basic() {2663 t := td.NewT(&testing.T{})2664 got := &struct {2665 Fullname string `json:"fullname"`2666 Age int `json:"age"`2667 }{2668 Fullname: "Bob",2669 Age: 42,2670 }2671 ok := t.SubJSONOf(got, `{"age":42,"fullname":"Bob","gender":"male"}`, nil)2672 fmt.Println("check got with age then fullname:", ok)2673 ok = t.SubJSONOf(got, `{"fullname":"Bob","age":42,"gender":"male"}`, nil)2674 fmt.Println("check got with fullname then age:", ok)2675 ok = t.SubJSONOf(got, `2676// This should be the JSON representation of a struct2677{2678 // A person:2679 "fullname": "Bob", // The name of this person2680 "age": 42, /* The age of this person:2681 - 42 of course2682 - to demonstrate a multi-lines comment */2683 "gender": "male" // This field is ignored as SubJSONOf2684}`, nil)2685 fmt.Println("check got with nicely formatted and commented JSON:", ok)2686 ok = t.SubJSONOf(got, `{"fullname":"Bob","gender":"male"}`, nil)2687 fmt.Println("check got without age field:", ok)2688 // Output:2689 // check got with age then fullname: true2690 // check got with fullname then age: true2691 // check got with nicely formatted and commented JSON: true2692 // check got without age field: false2693}2694func ExampleT_SubJSONOf_placeholders() {2695 t := td.NewT(&testing.T{})2696 got := &struct {2697 Fullname string `json:"fullname"`2698 Age int `json:"age"`2699 }{2700 Fullname: "Bob Foobar",2701 Age: 42,2702 }2703 ok := t.SubJSONOf(got, `{"age": $1, "fullname": $2, "gender": $3}`, []any{42, "Bob Foobar", "male"})2704 fmt.Println("check got with numeric placeholders without operators:", ok)2705 ok = t.SubJSONOf(got, `{"age": $1, "fullname": $2, "gender": $3}`, []any{td.Between(40, 45), td.HasSuffix("Foobar"), td.NotEmpty()})2706 fmt.Println("check got with numeric placeholders:", ok)2707 ok = t.SubJSONOf(got, `{"age": "$1", "fullname": "$2", "gender": "$3"}`, []any{td.Between(40, 45), td.HasSuffix("Foobar"), td.NotEmpty()})2708 fmt.Println("check got with double-quoted numeric placeholders:", ok)2709 ok = t.SubJSONOf(got, `{"age": $age, "fullname": $name, "gender": $gender}`, []any{td.Tag("age", td.Between(40, 45)), td.Tag("name", td.HasSuffix("Foobar")), td.Tag("gender", td.NotEmpty())})2710 fmt.Println("check got with named placeholders:", ok)2711 ok = t.SubJSONOf(got, `{"age": $^NotZero, "fullname": $^NotEmpty, "gender": $^NotEmpty}`, nil)2712 fmt.Println("check got with operator shortcuts:", ok)2713 // Output:2714 // check got with numeric placeholders without operators: true2715 // check got with numeric placeholders: true2716 // check got with double-quoted numeric placeholders: true2717 // check got with named placeholders: true2718 // check got with operator shortcuts: true2719}2720func ExampleT_SubJSONOf_file() {2721 t := td.NewT(&testing.T{})2722 got := &struct {2723 Fullname string `json:"fullname"`2724 Age int `json:"age"`2725 Gender string `json:"gender"`2726 }{2727 Fullname: "Bob Foobar",2728 Age: 42,2729 Gender: "male",2730 }2731 tmpDir, err := os.MkdirTemp("", "")2732 if err != nil {2733 t.Fatal(err)2734 }2735 defer os.RemoveAll(tmpDir) // clean up2736 filename := tmpDir + "/test.json"2737 if err = os.WriteFile(filename, []byte(`2738{2739 "fullname": "$name",2740 "age": "$age",2741 "gender": "$gender",2742 "details": {2743 "city": "TestCity",2744 "zip": 6662745 }2746}`), 0644); err != nil {2747 t.Fatal(err)2748 }2749 // OK let's test with this file2750 ok := t.SubJSONOf(got, filename, []any{td.Tag("name", td.HasPrefix("Bob")), td.Tag("age", td.Between(40, 45)), td.Tag("gender", td.Re(`^(male|female)\z`))})2751 fmt.Println("Full match from file name:", ok)2752 // When the file is already open2753 file, err := os.Open(filename)2754 if err != nil {2755 t.Fatal(err)2756 }2757 ok = t.SubJSONOf(got, file, []any{td.Tag("name", td.HasPrefix("Bob")), td.Tag("age", td.Between(40, 45)), td.Tag("gender", td.Re(`^(male|female)\z`))})2758 fmt.Println("Full match from io.Reader:", ok)2759 // Output:2760 // Full match from file name: true2761 // Full match from io.Reader: true2762}2763func ExampleT_SubMapOf_map() {2764 t := td.NewT(&testing.T{})2765 got := map[string]int{"foo": 12, "bar": 42}2766 ok := t.SubMapOf(got, map[string]int{"bar": 42}, td.MapEntries{"foo": td.Lt(15), "zip": 666},2767 "checks map %v is included in expected keys/values", got)2768 fmt.Println(ok)2769 // Output:2770 // true2771}2772func ExampleT_SubMapOf_typedMap() {2773 t := td.NewT(&testing.T{})2774 type MyMap map[string]int2775 got := MyMap{"foo": 12, "bar": 42}2776 ok := t.SubMapOf(got, MyMap{"bar": 42}, td.MapEntries{"foo": td.Lt(15), "zip": 666},2777 "checks typed map %v is included in expected keys/values", got)2778 fmt.Println(ok)2779 ok = t.SubMapOf(&got, &MyMap{"bar": 42}, td.MapEntries{"foo": td.Lt(15), "zip": 666},2780 "checks pointed typed map %v is included in expected keys/values", got)2781 fmt.Println(ok)2782 // Output:2783 // true2784 // true2785}2786func ExampleT_SubSetOf() {2787 t := td.NewT(&testing.T{})2788 got := []int{1, 3, 5, 8, 8, 1, 2}2789 // Matches as all items are expected, ignoring duplicates2790 ok := t.SubSetOf(got, []any{1, 2, 3, 4, 5, 6, 7, 8},2791 "checks at least all items are present, in any order, ignoring duplicates")2792 fmt.Println(ok)2793 // Tries its best to not raise an error when a value can be matched2794 // by several SubSetOf entries2795 ok = t.SubSetOf(got, []any{td.Between(1, 4), 3, td.Between(2, 10), td.Gt(100)},2796 "checks at least all items are present, in any order, ignoring duplicates")2797 fmt.Println(ok)2798 // When expected is already a non-[]any slice, it cannot be2799 // flattened directly using expected... without copying it to a new2800 // []any slice, then use td.Flatten!2801 expected := []int{1, 2, 3, 4, 5, 6, 7, 8}2802 ok = t.SubSetOf(got, []any{td.Flatten(expected)},2803 "checks at least all expected items are present, in any order, ignoring duplicates")2804 fmt.Println(ok)2805 // Output:2806 // true2807 // true2808 // true2809}2810func ExampleT_SuperBagOf() {2811 t := td.NewT(&testing.T{})2812 got := []int{1, 3, 5, 8, 8, 1, 2}2813 ok := t.SuperBagOf(got, []any{8, 5, 8},2814 "checks the items are present, in any order")2815 fmt.Println(ok)2816 ok = t.SuperBagOf(got, []any{td.Gt(5), td.Lte(2)},2817 "checks at least 2 items of %v match", got)2818 fmt.Println(ok)2819 // When expected is already a non-[]any slice, it cannot be2820 // flattened directly using expected... without copying it to a new2821 // []any slice, then use td.Flatten!2822 expected := []int{8, 5, 8}2823 ok = t.SuperBagOf(got, []any{td.Flatten(expected)},2824 "checks the expected items are present, in any order")2825 fmt.Println(ok)2826 // Output:2827 // true2828 // true2829 // true2830}2831func ExampleT_SuperJSONOf_basic() {2832 t := td.NewT(&testing.T{})2833 got := &struct {2834 Fullname string `json:"fullname"`2835 Age int `json:"age"`2836 Gender string `json:"gender"`2837 City string `json:"city"`2838 Zip int `json:"zip"`2839 }{2840 Fullname: "Bob",2841 Age: 42,2842 Gender: "male",2843 City: "TestCity",2844 Zip: 666,2845 }2846 ok := t.SuperJSONOf(got, `{"age":42,"fullname":"Bob","gender":"male"}`, nil)2847 fmt.Println("check got with age then fullname:", ok)2848 ok = t.SuperJSONOf(got, `{"fullname":"Bob","age":42,"gender":"male"}`, nil)2849 fmt.Println("check got with fullname then age:", ok)2850 ok = t.SuperJSONOf(got, `2851// This should be the JSON representation of a struct2852{2853 // A person:2854 "fullname": "Bob", // The name of this person2855 "age": 42, /* The age of this person:2856 - 42 of course2857 - to demonstrate a multi-lines comment */2858 "gender": "male" // The gender!2859}`, nil)2860 fmt.Println("check got with nicely formatted and commented JSON:", ok)2861 ok = t.SuperJSONOf(got, `{"fullname":"Bob","gender":"male","details":{}}`, nil)2862 fmt.Println("check got with details field:", ok)2863 // Output:2864 // check got with age then fullname: true2865 // check got with fullname then age: true2866 // check got with nicely formatted and commented JSON: true2867 // check got with details field: false2868}2869func ExampleT_SuperJSONOf_placeholders() {2870 t := td.NewT(&testing.T{})2871 got := &struct {2872 Fullname string `json:"fullname"`2873 Age int `json:"age"`2874 Gender string `json:"gender"`2875 City string `json:"city"`2876 Zip int `json:"zip"`2877 }{2878 Fullname: "Bob Foobar",2879 Age: 42,2880 Gender: "male",2881 City: "TestCity",2882 Zip: 666,2883 }2884 ok := t.SuperJSONOf(got, `{"age": $1, "fullname": $2, "gender": $3}`, []any{42, "Bob Foobar", "male"})2885 fmt.Println("check got with numeric placeholders without operators:", ok)2886 ok = t.SuperJSONOf(got, `{"age": $1, "fullname": $2, "gender": $3}`, []any{td.Between(40, 45), td.HasSuffix("Foobar"), td.NotEmpty()})2887 fmt.Println("check got with numeric placeholders:", ok)2888 ok = t.SuperJSONOf(got, `{"age": "$1", "fullname": "$2", "gender": "$3"}`, []any{td.Between(40, 45), td.HasSuffix("Foobar"), td.NotEmpty()})2889 fmt.Println("check got with double-quoted numeric placeholders:", ok)2890 ok = t.SuperJSONOf(got, `{"age": $age, "fullname": $name, "gender": $gender}`, []any{td.Tag("age", td.Between(40, 45)), td.Tag("name", td.HasSuffix("Foobar")), td.Tag("gender", td.NotEmpty())})2891 fmt.Println("check got with named placeholders:", ok)2892 ok = t.SuperJSONOf(got, `{"age": $^NotZero, "fullname": $^NotEmpty, "gender": $^NotEmpty}`, nil)2893 fmt.Println("check got with operator shortcuts:", ok)2894 // Output:2895 // check got with numeric placeholders without operators: true2896 // check got with numeric placeholders: true2897 // check got with double-quoted numeric placeholders: true2898 // check got with named placeholders: true2899 // check got with operator shortcuts: true2900}2901func ExampleT_SuperJSONOf_file() {2902 t := td.NewT(&testing.T{})2903 got := &struct {2904 Fullname string `json:"fullname"`2905 Age int `json:"age"`2906 Gender string `json:"gender"`2907 City string `json:"city"`2908 Zip int `json:"zip"`2909 }{2910 Fullname: "Bob Foobar",2911 Age: 42,2912 Gender: "male",2913 City: "TestCity",2914 Zip: 666,2915 }2916 tmpDir, err := os.MkdirTemp("", "")2917 if err != nil {2918 t.Fatal(err)2919 }2920 defer os.RemoveAll(tmpDir) // clean up2921 filename := tmpDir + "/test.json"2922 if err = os.WriteFile(filename, []byte(`2923{2924 "fullname": "$name",2925 "age": "$age",2926 "gender": "$gender"2927}`), 0644); err != nil {2928 t.Fatal(err)2929 }2930 // OK let's test with this file2931 ok := t.SuperJSONOf(got, filename, []any{td.Tag("name", td.HasPrefix("Bob")), td.Tag("age", td.Between(40, 45)), td.Tag("gender", td.Re(`^(male|female)\z`))})2932 fmt.Println("Full match from file name:", ok)2933 // When the file is already open2934 file, err := os.Open(filename)2935 if err != nil {2936 t.Fatal(err)2937 }2938 ok = t.SuperJSONOf(got, file, []any{td.Tag("name", td.HasPrefix("Bob")), td.Tag("age", td.Between(40, 45)), td.Tag("gender", td.Re(`^(male|female)\z`))})2939 fmt.Println("Full match from io.Reader:", ok)2940 // Output:2941 // Full match from file name: true2942 // Full match from io.Reader: true2943}2944func ExampleT_SuperMapOf_map() {2945 t := td.NewT(&testing.T{})2946 got := map[string]int{"foo": 12, "bar": 42, "zip": 89}2947 ok := t.SuperMapOf(got, map[string]int{"bar": 42}, td.MapEntries{"foo": td.Lt(15)},2948 "checks map %v contains at leat all expected keys/values", got)2949 fmt.Println(ok)2950 // Output:2951 // true2952}2953func ExampleT_SuperMapOf_typedMap() {2954 t := td.NewT(&testing.T{})2955 type MyMap map[string]int2956 got := MyMap{"foo": 12, "bar": 42, "zip": 89}...
td_json_test.go
Source:td_json_test.go
...13 "time"14 "github.com/maxatome/go-testdeep/internal/test"15 "github.com/maxatome/go-testdeep/td"16)17type errReader struct{}18// Read implements io.Reader.19func (r errReader) Read(p []byte) (int, error) {20 return 0, errors.New("an error occurred")21}22func TestJSON(t *testing.T) {23 type MyStruct struct {24 Name string `json:"name"`25 Age uint `json:"age"`26 Gender string `json:"gender"`27 }28 //29 // nil30 checkOK(t, nil, td.JSON(`null`))31 checkOK(t, (*int)(nil), td.JSON(`null`))32 //33 // Basic types34 checkOK(t, 123, td.JSON(` 123 `))35 checkOK(t, true, td.JSON(` true `))36 checkOK(t, false, td.JSON(` false `))37 checkOK(t, "foobar", td.JSON(` "foobar" `))38 //39 // struct40 //41 got := MyStruct{Name: "Bob", Age: 42, Gender: "male"}42 // No placeholder43 checkOK(t, got,44 td.JSON(`{"name":"Bob","age":42,"gender":"male"}`))45 checkOK(t, got, td.JSON(`$1`, got)) // json.Marshal() got for $146 // Numeric placeholders47 checkOK(t, got,48 td.JSON(`{"name":"$1","age":$2,"gender":$3}`,49 "Bob", 42, "male")) // raw values50 checkOK(t, got,51 td.JSON(`{"name":"$1","age":$2,"gender":"$3"}`,52 td.Re(`^Bob`),53 td.Between(40, 45),54 td.NotEmpty()))55 // Same using Flatten56 checkOK(t, got,57 td.JSON(`{"name":"$1","age":$2,"gender":"$3"}`,58 td.Re(`^Bob`),59 td.Flatten([]td.TestDeep{td.Between(40, 45), td.NotEmpty()}),60 ))61 // Operators are not JSON marshallable62 checkOK(t, got,63 td.JSON(`$1`, map[string]any{64 "name": td.Re(`^Bob`),65 "age": 42,66 "gender": td.NotEmpty(),67 }))68 // Placeholder + unmarshal before comparison69 checkOK(t, json.RawMessage(`[1,2,3]`), td.JSON(`$1`, []int{1, 2, 3}))70 checkOK(t, json.RawMessage(`{"foo":[1,2,3]}`),71 td.JSON(`{"foo":$1}`, []int{1, 2, 3}))72 checkOK(t, json.RawMessage(`[1,2,3]`),73 td.JSON(`$1`, []any{1, td.Between(1, 3), 3}))74 // Tag placeholders75 checkOK(t, got,76 td.JSON(`{"name":"$name","age":$age,"gender":$gender}`,77 // raw values78 td.Tag("name", "Bob"), td.Tag("age", 42), td.Tag("gender", "male")))79 checkOK(t, got,80 td.JSON(`{"name":"$name","age":$age,"gender":"$gender"}`,81 td.Tag("name", td.Re(`^Bo`)),82 td.Tag("age", td.Between(40, 45)),83 td.Tag("gender", td.NotEmpty())))84 // Tag placeholders + operators are not JSON marshallable85 checkOK(t, got,86 td.JSON(`$all`, td.Tag("all", map[string]any{87 "name": td.Re(`^Bob`),88 "age": 42,89 "gender": td.NotEmpty(),90 })))91 // Tag placeholders + nil92 checkOK(t, nil, td.JSON(`$all`, td.Tag("all", nil)))93 // Mixed placeholders + operator94 for _, op := range []string{95 "NotEmpty",96 "NotEmpty()",97 "$^NotEmpty",98 "$^NotEmpty()",99 `"$^NotEmpty"`,100 `"$^NotEmpty()"`,101 `r<$^NotEmpty>`,102 `r<$^NotEmpty()>`,103 } {104 checkOK(t, got,105 td.JSON(`{"name":"$name","age":$1,"gender":`+op+`}`,106 td.Tag("age", td.Between(40, 45)),107 td.Tag("name", td.Re(`^Bob`))),108 "using operator %s", op)109 }110 checkOK(t, got,111 td.JSON(`{"name":Re("^Bo\\w"),"age":Between(40,45),"gender":NotEmpty()}`))112 checkOK(t, got,113 td.JSON(`114{115 "name": All(Re("^Bo\\w"), HasPrefix("Bo"), HasSuffix("ob")),116 "age": Between(40,45),117 "gender": NotEmpty()118}`))119 checkOK(t, got,120 td.JSON(`121{122 "name": All(Re("^Bo\\w"), HasPrefix("Bo"), HasSuffix("ob")),123 "age": Between(40,45),124 "gender": NotEmpty125}`))126 // Same but operators in strings using "$^"127 checkOK(t, got,128 td.JSON(`{"name":Re("^Bo\\w"),"age":"$^Between(40,45)","gender":"$^NotEmpty()"}`))129 checkOK(t, got, // using classic "" string, so each \ has to be escaped130 td.JSON(`131{132 "name": "$^All(Re(\"^Bo\\\\w\"), HasPrefix(\"Bo\"), HasSuffix(\"ob\"))",133 "age": "$^Between(40,45)",134 "gender": "$^NotEmpty()",135}`))136 checkOK(t, got, // using raw strings, no escape needed137 td.JSON(`138{139 "name": "$^All(Re(r(^Bo\\w)), HasPrefix(r{Bo}), HasSuffix(r'ob'))",140 "age": "$^Between(40,45)",141 "gender": "$^NotEmpty()",142}`))143 // â¦with commentsâ¦144 checkOK(t, got,145 td.JSON(`146// This should be the JSON representation of MyStruct struct147{148 // A person:149 "name": "$name", // The name of this person150 "age": $1, /* The age of this person:151 - placeholder unquoted, but could be without152 any change153 - to demonstrate a multi-lines comment */154 "gender": $^NotEmpty // Operator NotEmpty155}`,156 td.Tag("age", td.Between(40, 45)),157 td.Tag("name", td.Re(`^Bob`))))158 before := time.Now()159 timeGot := map[string]time.Time{"created_at": time.Now()}160 checkOK(t, timeGot,161 td.JSON(`{"created_at": Between($1, $2)}`, before, time.Now()))162 checkOK(t, timeGot,163 td.JSON(`{"created_at": $1}`, td.Between(before, time.Now())))164 // Len165 checkOK(t, []int{1, 2, 3}, td.JSON(`Len(3)`))166 //167 // []byte168 checkOK(t, got,169 td.JSON([]byte(`{"name":"$name","age":$1,"gender":"male"}`),170 td.Tag("age", td.Between(40, 45)),171 td.Tag("name", td.Re(`^Bob`))))172 //173 // nil++174 checkOK(t, nil, td.JSON(`$1`, nil))175 checkOK(t, (*int)(nil), td.JSON(`$1`, td.Nil()))176 checkOK(t, nil, td.JSON(`$x`, td.Tag("x", nil)))177 checkOK(t, (*int)(nil), td.JSON(`$x`, td.Tag("x", nil)))178 checkOK(t, json.RawMessage(`{"foo": null}`), td.JSON(`{"foo": null}`))179 checkOK(t,180 json.RawMessage(`{"foo": null}`),181 td.JSON(`{"foo": $1}`, nil))182 checkOK(t,183 json.RawMessage(`{"foo": null}`),184 td.JSON(`{"foo": $1}`, td.Nil()))185 checkOK(t,186 json.RawMessage(`{"foo": null}`),187 td.JSON(`{"foo": $x}`, td.Tag("x", nil)))188 checkOK(t,189 json.RawMessage(`{"foo": null}`),190 td.JSON(`{"foo": $x}`, td.Tag("x", td.Nil())))191 //192 // Loading a file193 tmpDir := t.TempDir()194 filename := tmpDir + "/test.json"195 err := os.WriteFile(196 filename, []byte(`{"name":$name,"age":$1,"gender":$^NotEmpty}`), 0644)197 if err != nil {198 t.Fatal(err)199 }200 checkOK(t, got,201 td.JSON(filename,202 td.Tag("age", td.Between(40, 45)),203 td.Tag("name", td.Re(`^Bob`))))204 //205 // Reading (a file)206 tmpfile, err := os.Open(filename)207 if err != nil {208 t.Fatal(err)209 }210 checkOK(t, got,211 td.JSON(tmpfile,212 td.Tag("age", td.Between(40, 45)),213 td.Tag("name", td.Re(`^Bob`))))214 tmpfile.Close()215 //216 // Escaping $ in strings217 checkOK(t, "$test", td.JSON(`"$$test"`))218 //219 // Errors220 checkError(t, func() {}, td.JSON(`null`),221 expectedError{222 Message: mustBe("json.Marshal failed"),223 Summary: mustContain("json: unsupported type"),224 })225 checkError(t, map[string]string{"zip": "pipo"},226 td.All(td.JSON(`SuperMapOf({"zip":$1})`, "bingo")),227 expectedError{228 Path: mustBe(`DATA`),229 Message: mustBe("compared (part 1 of 1)"),230 Got: mustBe(`(map[string]string) (len=1) {231 (string) (len=3) "zip": (string) (len=4) "pipo"232}`),233 Expected: mustBe(`JSON(SuperMapOf(map[string]interface {}{234 "zip": "bingo",235 }))`),236 Origin: &expectedError{237 Path: mustBe(`DATA<All#1/1>["zip"]`),238 Message: mustBe(`values differ`),239 Got: mustBe(`"pipo"`),240 Expected: mustBe(`"bingo"`),241 },242 })243 //244 // Fatal errors245 checkError(t, "never tested",246 td.JSON("uNkNoWnFiLe.json"),247 expectedError{248 Message: mustBe("bad usage of JSON operator"),249 Path: mustBe("DATA"),250 Summary: mustContain("JSON file uNkNoWnFiLe.json cannot be read: "),251 })252 checkError(t, "never tested",253 td.JSON(42),254 expectedError{255 Message: mustBe("bad usage of JSON operator"),256 Path: mustBe("DATA"),257 Summary: mustBe("usage: JSON(STRING_JSON|STRING_FILENAME|[]byte|io.Reader, ...), but received int as 1st parameter"),258 })259 checkError(t, "never tested",260 td.JSON(errReader{}),261 expectedError{262 Message: mustBe("bad usage of JSON operator"),263 Path: mustBe("DATA"),264 Summary: mustBe("JSON read error: an error occurred"),265 })266 checkError(t, "never tested",267 td.JSON(`pipo`),268 expectedError{269 Message: mustBe("bad usage of JSON operator"),270 Path: mustBe("DATA"),271 Summary: mustContain("JSON unmarshal error: "),272 })273 checkError(t, "never tested",274 td.JSON(`[$foo]`,...
td_smuggle_test.go
Source:td_smuggle_test.go
...16 "github.com/maxatome/go-testdeep/internal/ctxerr"17 "github.com/maxatome/go-testdeep/internal/test"18 "github.com/maxatome/go-testdeep/td"19)20// reArmReader is a bytes.Reader that re-arms when an error occurs,21// typically on EOF.22type reArmReader bytes.Reader23var _ io.Reader = (*reArmReader)(nil)24func newReArmReader(b []byte) *reArmReader {25 return (*reArmReader)(bytes.NewReader(b))26}27func (r *reArmReader) Read(b []byte) (n int, err error) {28 n, err = (*bytes.Reader)(r).Read(b)29 if err != nil {30 (*bytes.Reader)(r).Seek(0, io.SeekStart) //nolint: errcheck31 }32 return33}34func (r *reArmReader) String() string { return "<no string here>" }35func TestSmuggle(t *testing.T) {36 num := 4237 gotStruct := MyStruct{38 MyStructMid: MyStructMid{39 MyStructBase: MyStructBase{40 ValBool: true,41 },42 ValStr: "foobar",43 },44 ValInt: 123,45 Ptr: &num,46 }47 gotTime, err := time.Parse(time.RFC3339, "2018-05-23T12:13:14Z")48 if err != nil {49 t.Fatal(err)50 }51 //52 // One returned value53 checkOK(t,54 gotTime,55 td.Smuggle(56 func(date time.Time) int {57 return date.Year()58 },59 td.Between(2010, 2020)))60 checkOK(t,61 gotStruct,62 td.Smuggle(63 func(s MyStruct) td.SmuggledGot {64 return td.SmuggledGot{65 Name: "ValStr",66 Got: s.ValStr,67 }68 },69 td.Contains("oob")))70 checkOK(t,71 gotStruct,72 td.Smuggle(73 func(s MyStruct) *td.SmuggledGot {74 return &td.SmuggledGot{75 Name: "ValStr",76 Got: s.ValStr,77 }78 },79 td.Contains("oob")))80 //81 // 2 returned values82 checkOK(t,83 gotStruct,84 td.Smuggle(85 func(s MyStruct) (string, bool) {86 if s.ValStr == "" {87 return "", false88 }89 return s.ValStr, true90 },91 td.Contains("oob")))92 checkOK(t,93 gotStruct,94 td.Smuggle(95 func(s MyStruct) (td.SmuggledGot, bool) {96 if s.ValStr == "" {97 return td.SmuggledGot{}, false98 }99 return td.SmuggledGot{100 Name: "ValStr",101 Got: s.ValStr,102 }, true103 },104 td.Contains("oob")))105 checkOK(t,106 gotStruct,107 td.Smuggle(108 func(s MyStruct) (*td.SmuggledGot, bool) {109 if s.ValStr == "" {110 return nil, false111 }112 return &td.SmuggledGot{113 Name: "ValStr",114 Got: s.ValStr,115 }, true116 },117 td.Contains("oob")))118 //119 // 3 returned values120 checkOK(t,121 gotStruct,122 td.Smuggle(123 func(s MyStruct) (string, bool, string) {124 if s.ValStr == "" {125 return "", false, "ValStr must not be empty"126 }127 return s.ValStr, true, ""128 },129 td.Contains("oob")))130 checkOK(t,131 gotStruct,132 td.Smuggle(133 func(s MyStruct) (td.SmuggledGot, bool, string) {134 if s.ValStr == "" {135 return td.SmuggledGot{}, false, "ValStr must not be empty"136 }137 return td.SmuggledGot{138 Name: "ValStr",139 Got: s.ValStr,140 }, true, ""141 },142 td.Contains("oob")))143 checkOK(t,144 gotStruct,145 td.Smuggle(146 func(s MyStruct) (*td.SmuggledGot, bool, string) {147 if s.ValStr == "" {148 return nil, false, "ValStr must not be empty"149 }150 return &td.SmuggledGot{151 Name: "ValStr",152 Got: s.ValStr,153 }, true, ""154 },155 td.Contains("oob")))156 //157 // Convertible types158 checkOK(t, 123,159 td.Smuggle(func(n float64) int { return int(n) }, 123))160 type xInt int161 checkOK(t, xInt(123),162 td.Smuggle(func(n int) int64 { return int64(n) }, int64(123)))163 checkOK(t, xInt(123),164 td.Smuggle(func(n uint32) int64 { return int64(n) }, int64(123)))165 checkOK(t, int32(123),166 td.Smuggle(func(n int64) int { return int(n) }, 123))167 checkOK(t, gotTime,168 td.Smuggle(func(t fmt.Stringer) string { return t.String() },169 "2018-05-23 12:13:14 +0000 UTC"))170 checkOK(t, []byte("{}"),171 td.Smuggle(172 func(x json.RawMessage) json.RawMessage { return x },173 td.JSON(`{}`)))174 //175 // bytes slice caster variations176 checkOK(t, []byte(`{"foo":1}`),177 td.Smuggle(json.RawMessage{}, td.JSON(`{"foo":1}`)))178 checkOK(t, []byte(`{"foo":1}`),179 td.Smuggle(json.RawMessage(nil), td.JSON(`{"foo":1}`)))180 checkOK(t, []byte(`{"foo":1}`),181 td.Smuggle(reflect.TypeOf(json.RawMessage(nil)), td.JSON(`{"foo":1}`)))182 checkOK(t, `{"foo":1}`,183 td.Smuggle(json.RawMessage{}, td.JSON(`{"foo":1}`)))184 checkOK(t, newReArmReader([]byte(`{"foo":1}`)), // io.Reader first185 td.Smuggle(json.RawMessage{}, td.JSON(`{"foo":1}`)))186 checkError(t, nil,187 td.Smuggle(json.RawMessage{}, td.JSON(`{}`)),188 expectedError{189 Message: mustBe("incompatible parameter type"),190 Path: mustBe("DATA"),191 Got: mustBe("nil"),192 Expected: mustBe("json.RawMessage or convertible or io.Reader"),193 })194 checkError(t, MyStruct{},195 td.Smuggle(json.RawMessage{}, td.JSON(`{}`)),196 expectedError{197 Message: mustBe("incompatible parameter type"),198 Path: mustBe("DATA"),199 Got: mustBe("td_test.MyStruct"),200 Expected: mustBe("json.RawMessage or convertible or io.Reader"),201 })202 checkError(t, errReader{}, // erroneous io.Reader203 td.Smuggle(json.RawMessage{}, td.JSON(`{}`)),204 expectedError{205 Message: mustBe("an error occurred while reading from io.Reader"),206 Path: mustBe("DATA"),207 Summary: mustBe("an error occurred"),208 })209 //210 // strings caster variations211 type myString string212 checkOK(t, `pipo bingo`,213 td.Smuggle("", td.HasSuffix("bingo")))214 checkOK(t, []byte(`pipo bingo`),215 td.Smuggle(myString(""), td.HasSuffix("bingo")))216 checkOK(t, []byte(`pipo bingo`),217 td.Smuggle(reflect.TypeOf(myString("")), td.HasSuffix("bingo")))218 checkOK(t, newReArmReader([]byte(`pipo bingo`)), // io.Reader first219 td.Smuggle(myString(""), td.HasSuffix("bingo")))220 checkError(t, nil,221 td.Smuggle("", "bingo"),222 expectedError{223 Message: mustBe("incompatible parameter type"),224 Path: mustBe("DATA"),225 Got: mustBe("nil"),226 Expected: mustBe("string or convertible or io.Reader"),227 })228 checkError(t, MyStruct{},229 td.Smuggle(myString(""), "bingo"),230 expectedError{231 Message: mustBe("incompatible parameter type"),232 Path: mustBe("DATA"),233 Got: mustBe("td_test.MyStruct"),234 Expected: mustBe("td_test.myString or convertible or io.Reader"),235 })236 checkError(t, errReader{}, // erroneous io.Reader237 td.Smuggle("", "bingo"),238 expectedError{239 Message: mustBe("an error occurred while reading from io.Reader"),240 Path: mustBe("DATA"),241 Summary: mustBe("an error occurred"),242 })243 //244 // Any other caster variations245 checkOK(t, `pipo bingo`,246 td.Smuggle([]rune{}, td.Contains([]rune(`bing`))))247 checkOK(t, `pipo bingo`,248 td.Smuggle(([]rune)(nil), td.Contains([]rune(`bing`))))249 checkOK(t, `pipo bingo`,250 td.Smuggle(reflect.TypeOf([]rune{}), td.Contains([]rune(`bing`))))251 checkOK(t, 123.456, td.Smuggle(int64(0), int64(123)))252 checkOK(t, 123.456, td.Smuggle(reflect.TypeOf(int64(0)), int64(123)))253 //...
Read
Using AI Code Generation
1import (2func main() {3 t := td.New()4 fmt.Println(t.Read())5}6import (7func main() {8 t := td.New()9 fmt.Println(t.Write())10}11import (
Read
Using AI Code Generation
1import (2func main() {3 td.Read()4 fmt.Println("Name: ", td.Name)5 fmt.Println("Roll: ", td.Roll)6 fmt.Println("Marks: ", td.Marks)7}8import (9func main() {10 td.Write()11}12import (13func main() {14 td.Append()15}16import (17func main() {18 td = td_test.ReadAll()19 for i := 0; i < len(td); i++ {20 fmt.Println("Name: ", td[i].Name)21 fmt.Println("Roll: ", td[i].Roll)
Read
Using AI Code Generation
1import (2func main() {3 td_test := td_test{}4 td_test.Read()5 fmt.Println("Hello, playground")6}7type td_test struct {8}9func (td_test *td_test) Read() {10}
Read
Using AI Code Generation
1import (2func main() {3 td := td_test.New()4 fmt.Println(td.Read())5}6import (7func main() {8 td := td_test.New()9 fmt.Println(td.x)10}11import (12func main() {13 td := td_test.New()14 fmt.Println(td.X)15}16import (17func main() {18 td := td_test.New()19 fmt.Println(td.X)20}21import (22func main() {23 td := td_test.New()24 fmt.Println(td.X)25}
Read
Using AI Code Generation
1import (2func main() {3 td.Read()4 fmt.Println(td)5}6{4}
Read
Using AI Code Generation
1import (2func main() {3 t.Read()4 fmt.Println("Name is: ", t.Name)5 fmt.Println("Age is: ", t.Age)6}7Related posts: Go | Packages in Go | Set 2 (using multiple packages) Go | Packages in Go | Set 3 (using internal packages) Go | Packages in Go | Set 1 (creating a package) Go | Packages in Go | Set 4 (using multiple packages) Go | Packages in Go | Set 5 (using multiple packages) Go | Packages in Go | Set 6 (using multiple packages) Go | Packages in Go | Set 7 (using multiple packages) Go | Packages in Go | Set 8 (using multiple packages) Go | Packages in Go | Set 9 (using multiple packages) Go | Packages in Go | Set 10 (using multiple packages) Go | Packages in Go | Set 11 (using multiple packages) Go | Packages in Go | Set 12 (using multiple packages) Go | Packages in Go | Set 13 (using multiple packages) Go | Packages in Go | Set 14 (using multiple packages) Go | Packages in Go | Set 15 (using multiple packages) Go | Packages in Go | Set 16 (using multiple packages) Go | Packages in Go | Set 17 (using multiple packages) Go | Packages in Go | Set 18 (using multiple packages) Go | Packages in Go | Set 19 (using multiple packages) Go | Packages in Go | Set 20 (using multiple packages) Go | Packages in Go | Set 21 (using multiple packages) Go | Packages in Go | Set 22 (using multiple packages) Go | Packages in Go |
Read
Using AI Code Generation
1func main() {2 td := td_test{}3 td.Read()4}5import (6type td_test struct {7}8func (td *td_test) Write() {9 f, err := os.Create("test.txt")10 if err != nil {11 fmt.Println(err)12 }13 _, err = f.WriteString("This is a test file")14 if err != nil {15 fmt.Println(err)16 f.Close()17 }18 err = f.Close()19 if err != nil {20 fmt.Println(err)21 }22 fmt.Println("File written successfully")23}24func main() {25 td := td_test{}26 td.Write()27}28import (29type td_test struct {30}31func (td *td_test) Read() {32 data, err := ioutil.ReadFile("test.txt")33 if err != nil {34 fmt.Println("File reading error", err)35 }36 fmt.Println("Contents of file:", string(data))37}38func main() {
Read
Using AI Code Generation
1import (2func main() {3 fmt.Println("Hello World")4 td_test.Read()5}6import (7func Read() {8 fmt.Println("Read method called")9}10import (11func Read() {12 fmt.Println("Read method called")13}14import (15func Read() {16 fmt.Println("Read method called")17}18import (
Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!