Test Engineering Notes
3.81K subscribers
177 photos
2 videos
647 links
Україномовний канал про технічні аспекти тестування, розподілені системи, блокчейн.

Консультації з автоматизації, менторинг, тестові співбесіди - @al8xr
Download Telegram
Python crash course для новачків

#python #beginner

Для тих, хто "входить" в Python не з повного нуля, маю дуже корисний ресурс.
Beginner Python cheat sheet - це цілий PDF з усіма базовими аспектами синтаксису, тестування, бібліотеками та навіть трошки фреймворком Django. Може стати у пригоді коли ви будете готуватися до співбесіди.

Взагалі це компіляція понять з книги Python Crash Course. Для книги є навіть окрема сторінка з усіма ресурсами, завданнями та іншим.

А поки, я шукаю (та знаходжу) книги з Python для рівня intermediate. (Де б було щось, окрім базового синтаксису)
Може у вас є рекомендації?
👍17🔥41👏1
Візуалізуємо виконання коду - без реєстрації та смс

#python #learning #junior

Всім привіт.

Коли ти вчиш свою першу мову програмування буває складно зрозуміти, що відбувається "під капотом" того чи іншого шматка коду. Авжеж можна (та й треба) дебажити.

Але я пропоную поглянути на інший спосіб візуалізації коду - це PythonTutor.
Підтримується не тільки Python, але й Java, JS (трохи куцо, але маємо те шо маємо).
Цей сайт буде корисним усім менторам та тим людям, що викладають мови програмування.
👍142
Які є інструменти для автоматизації в ...

#testing #python #java #tools

Доброго ранку.
Коли ми тільки знайомимось з автоматизацією, або ж переходимо з однієї мови програмування в іншу - постає багато питань.
- Яку бібліотеку для ассертів взяти в мові Х?
- Які є альтернативи бібліотеці репортів у мові У?
- Чи є BDD інструмент для мови …?
- Та інше …

Авжеж можна запитати в каналі чи в чатах тест інженерів. Але мені допомагають списки типу awesome-X. У них хтось дуже добрий вже зібрав купу інструментів та виклав у публічний доступ.

Які списки використовую я сам:
- awesome-test-automation від atinfo. Є для багатьох мов програмування - у тому числі - Java, Python, JS.
- awesome-python-testing - лист інструментів для Python.
- java-testing-toolbox - набір прикладів з книги “30 Testing Tools & Libraries Every Java Developer Must Know”. (Знайшов цього тижня, але виглядає непогано для початківців).

Для інших технологій та мов програмування, можна пошукати списки awesome списки на GitHub.
👍314🔥2
Приховане перетворення даних в grpcui та k6

#testing #api #tools #python

Ситуація
Для одного з наших gRPC сервісів нам потрібно відправити hash у форматі HEX. Але коли я намагався відправити запит за допомогою grpcui або ж у скрипті навантаження k6 - сервер повертав помилку, що такий хеш не знайдений в нашій базі.

Задача
Треба було розібратись, у чому причина перетворення даних - та де криється проблема. Бо сервіс точно працював правильно.
Значить проблема в роботі інтрументів ...

Рішення
Як виявилося - обидва інструменти очікують вхідні дані в base64. Потім вони декодують ці дані та надсилають результат на сервер.
В Python з base64 працювати дуже легко. То ж у нагоді стане наступний скрипт.

import base64


def from_b64_to_hex(input):
binary_data = base64.b64decode(input)
return binary_data.hex()


def from_hex_to_b64(input):
binary_data = bytes.fromhex(input)
return base64.b64encode(binary_data).decode()


base64_string = "LxTKPCw9jAv1U8Xm6lxjhtGlnoZzNPc6I="
hex_string = "2f14ca3c2c3d880653b15e6ea5c6386d1a59e867334f73a2"

assert hex_string == from_b64_to_hex(base64_string)
assert base64_string == from_hex_to_b64(hex_string)


І ще одне

Крім цього скрипта, можна скористатись також безкоштовним онлайн конвертером.
17👍4
Python Big O: the time complexities of different data structures in Python

#python #algo

Для тих, кому було цікаво розібратись яка складність (за часом виконання) в структурах даних Python.
👍14
50 shades of Fibonacci

#coding #interview #python

Одна з найчастіших задач, яку дають на перевірку навичок програмування автоматизатора на співбесіді - це обчислення послідовності Фібоначчі.

Для тих, хто забув - це послідовність типу 0, 1, 1, 2, 3, 5, 8, 13, 21, ..., що описується формулою:
F(n) = F(n-1) + F(n-2), де F(0) = 0 та F(1) = 1.

Виявляється, одну й ту саму задачу можна вирішити по-різному. Кожне рішення покаже ваш рівень розуміння задачі, мови програмування та тестування негативних кейсів.

Перед тим, як дивитись приклади - пропоную самим спробувати написати код.

1. Простий та наївний підхід - обчислюємо так, як написано у формулі (з рекурсією):
def fib(n: int) -> int:
return fib(n-1) + fib(n-2)


Але тут можна легко отримати RecursionError: maximum recursion depth exceeded

2. Покращуємо код, додаючі перевірку базових кейсів:
def fib(n: int) -> int:
if n < 2:
return n
return fib(n-1) + fib(n-2)


3. Можна також застосувати техніку мемоїзації (тобто замість обчислень знову й знову - запам'ятовуємо проміжні результати):
from typing import Dict

memo: Dict[int, int] = {0: 0, 1: 1}
def fib(n: int) -> int:
if n not in memo:
memo[n] = fib(n - 1) + fib(n - 2)
return memo[n]


4. Мемоїзація також є "вбудована" в сам Python:
from functools import lru_cache

@lru_cache(maxsize=None)
def fib(n: int) -> int:
if n < 2:
return n
return fib(n - 1) + fib(n - 2)


5. Замість рекурсії - можна вирішити задачу з циклом:
def fib(n: int) -> int:
if n == 0:
return n
last: int = 0
next: int = 1
for _ in range(1, n):
last, next = next, last + next
return next


P.S. Можна ще обчислити за допомогою генераторів, але цей спосіб розберемо в наступних нотатках.
👍35❤‍🔥41🥴1
Знайти співробітників з найбільшою зарплатнею

#python #interview #coding

Всім привіт.
Сьогодні я прийшов до вас із задачею. Цю задачу можна вирішити для тренування, а можна навіть дати на співбесіді. Бо задача вкрай нескладна та можна швидко отримати рішення. Відповідь покаже рівень знання мови програмування.

Задача


"Уявімо, що ви працюєте в HR департаменті великої компанії. Вам прилетіла задача написати новий ендпоінт, який буде повертати всіх співробітників, що мають ЗП більше, ніж $100,000 на рік. Поверніть результат у вигляді набору з двох елементів - імені працівника та його зарплати".

Вхідні дані - це map такого вигляду:

employees = {'Alice': 100000, 'Bob': 99817, 'Carol': 122908, 'Frank': 88123, 'Eve': 93121}


Рішення буде наприклад таким:

top_earners = []  
for key, val in employees.items():
    if val >= 100000:
        top_earners.append((key,val))
print(top_earners)


Але існує трохи більш елегантне рішення, що показує можливості мови Python:

top_earners = [(k, v) for k, v in employees.items() if v >= 100000]
print(top_earners)


Або навіть:

top_earners = list(filter(lambda x: x[1] >= 100000, employees.items()))
print(top_earners)


Як бачите, на кожну проблему може бути декілька варіантів рішень. Але не забувайте PEP20 - "There should be one-- and preferably only one --obvious way to do it."

А як буде виглядати рішення на вашій улюбленій мові програмування?
👍193🔥2
Коротко про базові типи даних в Python

#python

Сьогодні невеличкий розбір базових типів даних в Python. Здається, тема надто проста, але як завжди, є невеличкі деталі. Я наприклад не часто працюю з нескінченностями, тому не знав, шо у float можна отримати такі дані.

Цілочисленні - int. Подивитись, який же найбільший integer можна з sys.maxsize
import sys

a = 100500
print(f"a = {a} та має тип {type(a)}")
print(f"максимальний розмір типу int: {sys.maxsize}")


Замість окремих float / double - Python має тільки float.
b = 123.456
print(f"b = {b} та має тип {type(b)}")


Крім float, є можливість працювати комплексними числами, а також з decimal / fraction.
c = 100 + 5j
print(f"c = {c} та має тип {type(c)}")

from decimal import Decimal
from fractions import Fraction

dec = Decimal("0.1")
print(f"dec = {dec} та має тип {type(dec)}")

frac = Fraction(1, 3)
print(f"frac = {frac} та має тип {type(frac)}")

Float дозволяє отримати трохи більш цікаві дані - такі як NaN чи нескінченність.
nan = float("nan")
print(f"nan = {nan} та має тип {type(nan)}")
inf = float("inf")
neg_inf = float("-inf")
print(f"inf = {inf} та має тип {type(inf)}")
print(f"neg_inf = {neg_inf} та має тип {type(neg_inf)}")


Якщо не подобається float, можна застосувати math. Результат той же.
import math
print(f"Те ж саме з math.inf - {math.inf == inf}")


Строки в Python бувають трьох типів - звичайні, сирі (raw) та форматовані.
single_quote_string = 'Hello, World!'
double_quote_string = "Hello, World!"
print(f"Python дозволяє строки з будь-якими лапками: {single_quote_string == double_quote_string}")

raw_string = r"\tRaw \n string"
well_done_string = "\tWell-done \n string"
print(f"raw_string = {raw_string}")
print(f"well_done_string = {well_done_string}")

formatted = f"{a} + {b} = {a + b}"
print(f"formatted = {formatted}")
print(f"formatted з фігурними дужками = {{{formatted}}}")


Результат в консолі:
a = 100500 та має тип <class 'int'>
максимальний розмір типу int: 9223372036854775807
b = 123.456 та має тип <class 'float'>
c = (100+5j) та має тип <class 'complex'>
dec = 0.1 та має тип <class 'decimal.Decimal'>
frac = 1/3 та має тип <class 'fractions.Fraction'>
nan = nan та має тип <class 'float'>
inf = inf та має тип <class 'float'>
neg_inf = -inf та має тип <class 'float'>
Те ж саме з math.inf - True
Python дозволяє строки з будь-якими лапками: True
raw_string = \tRaw \n string
well_done_string = Well-done
string
formatted = 100500 + 123.456 = 100623.456
formatted з фігурними дужками = {100500 + 123.456 = 100623.456}
13👍6
Магія пакування в Python

#python

Розпакування - то одна з найцікавіших "фішок" мови Python.

Якщо у вас є tuple - то можна звичайно отримати доступ до елементів через індекс:
p = (4, 5)
first = p[0]
second = p[1]


Але можна - "розпакувати" tuple одразу в дві змінні
unpacked_first, unpacked_second = p


Можна розпаковувати більш складні структури:
data = ['ACME', 50, 91.1, (2024, 06, 18)]
name, shares, price, date = data


*
дозволяє вказати - "а все інше запиши сюди 😁". Наприклад тут - в phone_numbers
record = ('Alex', 'alex@example.com', '123-456-7890', '124-456-0000')
name, email, *phone_numbers = record


Подібним чином можна розбирати строки (якщо ви точно впевнені в їх структурі):
line = 'nobody:*:-2:-2:Unprivileged User:/var/empty:/usr/bin/false'
uname, *fields, homedir, sh = line.split(':')


_
дозволяє вказати, що ця частина даних несуттєва. Наприклад, отримати тільки імʼя та рік:
record = ('ACME', 50, 123.45, (12, 18, 2012))
name, *_, (*_, year) = record


З пакуванням звʼязаний відома задачка про те, як поміняти дві змінні місцями
a = 1
b = 2
a, b = b, a


Також, за допомогою рекурсії та розпакування можна писати такі функції
def sum(items):
head, *tail = items
return head + sum(tail) if tail else head


Як завжди - результати виконання кожного фрагменту:
first: 4, second: 5
unpacked_first: 4, unpacked_second: 5
name: ACME, shares: 50, price: 91.1, date: (2012, 12, 21)
name: Alex, email: alex@example.com, phone_numbers: ['123-456-7890', '124-456-0000']
uname: nobody, homedir: /var/empty, sh: /usr/bin/false
name: ACME, year: 2012
a: 2, b: 1
sum([1, 2, 3, 4, 5]) = 15


Весь код - тут.
11👍4🔥3
Про динамічність типізації

#python #engineering

Коли я ще писав на Java, я був в курсі, що існують статично-типізовані та динамічно-типізовані мови програмування.

В динамічно-типізованих мовах, як-от Python чи Javascript, тип перевіряється під час запуску.
Тому ніхто не забороняє писати щось типу такого:

x = 10       # x - integer
x = "Hello" # x - string


Можна навіть написати функцію, яка буде працювати з різними типами

def add(a, b):
return a + b

print(add(5, 10)) # Працює з integers
print(add("Hello, ", "Alex!")) # Працює з strings


В статично-типізованих мовах (C++, Java, C#) компілятор перевіряє правильність типів ще на етапі компіляції (до запуску коду).
Тому компілятор буде скаржитись як на етапі визначення змінної

int x = 10;        // x - integer
x = "Alex"; // Compile-time error: incompatible types


так і при роботі з функціями:

public int add(int a, int b) {
return a + b;
}

public static void main(String[] args) {
System.out.println(add(5, 10)); // Works with integers
System.out.println(add("Hello, ", "Alex!")); // Compile-time error
}


Погляд під іншим кутом

Протягом якогось часу я просто думав, що Python динамічно-типізований так само, як JS та не переймався.
Але в системі координат типізованості є ще одна вісь. Мова можна бути зі слабкою (weak) та сильною (strong) типізацією.

Python - мова з сильною типізацією. Інтерпретатор все-таки слідкує за типами та може генерувати TypeError у випадках жорсткого порушення правил.

a = "Hello"
b = 5
c = a + b # Спричинить TypeError: бо додавати можемо тільки str (не "int")

d = a + str(b) # Python вимагає явного перетворення даних
print(d) # Hello5


Те ж саме з функціями:

def add_numbers(a: int, b: int) -> int:
return a + b

result = add_numbers(3, "4") # Знову буде TypeError: unsupported operand type(s) for +: 'int' and 'str'


JS - це мова зі слабкою типізацією.
Компілятор дозволяє як завгодно працювати з типами та багато чого робить "під капотом" - неявно.
Саме через ці неявні перетворення в JS так багато "магії".

// звичайні динамічні типи
var a = 10;
a = "Now I'm a string"; // Тип a був змінений з number на string
console.log(a); // Output: Now I'm a string

// JS робить магію
var a = "Hello";
var b = 5;
var c = a + b; // Помилки не буде. JavaScript спробує сконвертувати b в строку та виконати конкатенацію
console.log(c); // Hello5

// Інший приклад
var e = 1;
var f = "2";
var g = e - f; // JavaScript робить число з f та віднімає
console.log(g); // Output: -1


Висновок

Хоч Python всі називають мовою, де можна як завгодно працювати з типами, це не так. Все-таки є в Python є деякі обмеження (може навіть на краще).
Найбільша свобода все-таки в JS. Але найбільша свобода може бути причиною найбільшої "головної болі" та багів.
18👍5
Корисні "трюки" в Python

#python #tricks

Сьогодні дозвольте поділитись прикладами трюків з книги "Python One-Liners".
Користуйтесь ними для навчання, або будьте готові отримати шось подібне на співбесіді (зустрічав таке).

1. У вас є строка з багатьма рядками. Перетворіть її на масив масивів слів в кожному рядку, якщо слово має більше трьох літер.
text = '''Call me Ishmael. Some years ago - never mind how long precisely - having  
little or no money in my purse, and nothing particular to interest me
on shore, I thought I would sail about a little and see the watery part
of the world. It is a way I have of driving off the spleen, and regulating
the circulation. - Moby Dick'''


Очікуваний результат:
[['Call', 'Ishmael.', 'Some', 'years', 'never', 'mind', 'long', 'precisely', 'having'], ['little', 'money', 'purse,', 'nothing', 'particular', 'interest'], ['shore,', 'thought', 'would', 'sail', 'about', 'little', 'watery', 'part'], ['world.', 'have', 'driving', 'spleen,', 'regulating'], ['circulation.', 'Moby', 'Dick']]


Рішення:
w = [[x for x in line.split() if len(x)>3] for line in text.split('\n')]


2. Прочитайте файл, видаліть для кожного строки непотрібні пробіли та збережіть це все в лист.

Рішення:
print([line.strip() for line in open("readFile.py")])


3. Маючи словник з даними щодо компаній та заробітних плат співробітників, поверніть список компаній, що платять менше ніж 9$ на годину.

companies = {  
    'CoolCompany' : {'Alice' : 33, 'Bob' : 28, 'Frank' : 29},
    'CheapCompany' : {'Ann' : 4, 'Lee' : 9, 'Chrisi' : 7},
    'SosoCompany' : {'Esther' : 38, 'Cole' : 8, 'Paris' : 18}
}


Рішення:
illegal = [x for x in companies if any(y<9 for y in companies[x].values())]


P.S. Трюки корисні, коли ви та ваші колеги розумієте як вони працюють.
18