Golang | Вопросы собесов
4.34K subscribers
27 photos
1 video
713 links
Download Telegram
🤔 Go — императивный или декларативный?

Это императивный язык программирования. Код пишется в виде последовательных команд, а не деклараций (как в SQL или Terraform). Однако в некоторых случаях (например, в Kubernetes) Go может использоваться в декларативном стиле.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 В чем разница между буферизированными и небуферизированными каналами?

Каналы — это мощные инструменты для обмена данными между горутинами, обеспечивающие синхронизацию и безопасную коммуникацию. Основное различие между буферизированными и небуферизированными каналами заключается в их поведении при отправке и получении данных.

🚩Небуферизированные каналы

Не имеют внутренней емкости, т.е. они не могут хранить значения. Эти каналы требуют, чтобы отправитель и получатель были готовы обмениваться данными одновременно. Если одна сторона не готова, другая будет заблокирована:
Отправка данных в него блокирует отправителя до тех пор, пока получатель не прочитает данные из канала.
Получение данных из него блокирует получателя до тех пор, пока другая горутина не отправит данные в канал.
ch := make(chan int) // Создание небуферизированного канала
go func() {
val := <-ch // Блокируется, ожидая данные
fmt.Println("Received:", val)
}()
ch <- 3 // Блокируется, пока данные не будут получены


🚩Буферизированные каналы

Имеют внутреннюю емкость, что позволяет хранить одно или несколько элементов без непосредственного получателя данных. Отправка или получение данных работает следующим образом:
Отправка блокируется, только если буфер заполнен. До этого момента данные могут быть отправлены без блокировки, даже если получатель не готов их принять.
Получение из буферизированного канала блокируется, только если канал пуст. Если в канале есть данные, получение происходит без блокировки.
ch := make(chan int, 2) // Создание буферизированного канала с емкостью 2
ch <- 1 // Отправка данных без блокировки
ch <- 2 // Отправка данных без блокировки
go func() {
val := <-ch // Получение данных без блокировки
fmt.Println("Received:", val)
}()


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Главный недостаток стандартного логгера?

Стандартный логгер (log из stdlib):
- Не поддерживает уровни логирования (INFO, ERROR).
- Не форматирует логи по умолчанию.
- Не поддерживает ротацию логов.
Для продакшена обычно используют logrus или zap.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Что такое EXPLAIN?

EXPLAIN — это команда в SQL, которая показывает, как база данных выполняет SQL-запрос. Она помогает оптимизировать запросы, анализируя их план выполнения.

🚩Как работает?

Когда вы пишете EXPLAIN перед SQL-запросом, база данных не выполняет его, а показывает пошаговый процесс выполнения. Это помогает понять:
Какие индексы используются
Какие таблицы сканируются
Какие соединения (JOIN) выполняются
Оценить производительность запроса
EXPLAIN SELECT * FROM users WHERE age > 30;


Выход (пример)
Seq Scan on users  (cost=0.00..25.00 rows=10 width=100)


🚩`EXPLAIN ANALYZE` — глубокий анализ

Добавляет реальное время выполнения запроса
EXPLAIN ANALYZE SELECT * FROM users WHERE age > 30;


🚩Оптимизация запросов с `EXPLAIN`

Использование индексов
CREATE INDEX idx_users_age ON users(age);


Правильный порядок JOIN
EXPLAIN SELECT * FROM orders JOIN users ON orders.user_id = users.id;


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Есть ли для Go хороший ORM?

Да, популярные ORM для Go:
- GORM – самый распространенный, удобный, но может быть медленным.
- Ent – более производительный, но требует генерации кода.
- SQLX – не ORM, но расширяет database/sql и упрощает работу с SQL.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Как преобразовать строку в int и наоборот?

В Go есть несколько способов преобразования строки в число и числа в строку.

🟠Преобразование строки в `int`
Используется пакет strconv и функция strconv.Atoi() или strconv.ParseInt().
package main

import (
"fmt"
"strconv"
)

func main() {
str := "42"
num, err := strconv.Atoi(str) // Преобразуем строку в int
if err != nil {
fmt.Println("Ошибка:", err)
return
}
fmt.Println("Число:", num) // Выведет: Число: 42
}


Пример 2: strconv.ParseInt() (гибкое преобразование)
num, err := strconv.ParseInt("1234", 10, 64) 
// "1234" → строка, 10 → десятичная система, 64 → int64

if err != nil {
fmt.Println("Ошибка:", err)
} else {
fmt.Println("Число:", num) // 1234
}


🟠Преобразование `int` в строку
Используется strconv.Itoa() или strconv.FormatInt().
num := 42
str := strconv.Itoa(num) // 42 → "42"
fmt.Println("Строка:", str) // "42"


Пример 2: strconv.FormatInt() (int64 → строка)
num := int64(12345)
str := strconv.FormatInt(num, 10) // 10 — десятичная система
fmt.Println("Строка:", str) // "12345"


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Стандартный набор метрик Prometheus в Go-программе?

Prometheus в Go-программах собирает такие стандартные метрики:
- Go runtime метрики (go_gc_duration_seconds, go_memstats_heap_alloc_bytes).
- HTTP-метрики (если используется prometheus/client_golang).
- Метрики запросов (http_requests_total, request_duration_seconds).
- Производительность CPU и памяти (process_cpu_seconds_total, process_resident_memory_bytes).
Для их сбора нужно подключить prometheus/client_golang и зарегистрировать метрики в /metrics.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Что такое SSL?

SSL (Secure Sockets Layer) — это протокол, который обеспечивает безопасность передачи данных в интернете, используя шифрование. Он был разработан для защиты данных, передаваемых между клиентом (например, веб-браузером) и сервером (например, веб-сайтом), от перехвата и манипуляций.

🚩Основные функции

🟠Шифрование:
Защита данных от перехвата и чтения посторонними лицами путем их шифрования.
🟠Аутентификация:
Подтверждение подлинности сервера (и иногда клиента) с помощью цифровых сертификатов, что позволяет клиенту убедиться, что он подключен к настоящему серверу.
🟠Целостность данных:
Проверка того, что данные не были изменены во время передачи, с помощью контрольных сумм и хеш-функций.

🚩Как работает

1⃣Установка соединения:
Клиент инициирует соединение с сервером, запрашивая защищенное соединение.
2⃣Передача сертификата:
Сервер отправляет свой цифровой сертификат, который содержит его публичный ключ и информацию о сервере.
3⃣Проверка сертификата:
Клиент проверяет сертификат, используя доверенные центры сертификации (CA), чтобы удостовериться в подлинности сервера.
4⃣Создание сессионных ключей:
Клиент и сервер используют асимметричное шифрование для обмена ключами сеанса, которые затем используются для симметричного шифрования данных в течение сессии.
5⃣Шифрование данных:
Все данные, передаваемые между клиентом и сервером, шифруются с использованием симметричных ключей, обеспечивая безопасность передачи.

🚩Пример использования

При посещении веб-сайта с использованием HTTPS (например, https://example.com), SSL обеспечивает шифрование и безопасность данных, передаваемых между вашим браузером и сервером.

🚩Развитие

🟠SSL 1.0:
Никогда не был выпущен публично из-за серьезных уязвимостей.
🟠SSL 2.0:
Выпущен в 1995 году, но вскоре был признан небезопасным из-за множества уязвимостей.
🟠SSL 3.0:
Выпущен в 1996 году, значительно улучшил безопасность, но со временем также был признан устаревшим из-за уязвимостей (например, POODLE-атака).

🚩TLS как наследник SSL

🟠TLS (Transport Layer Security):
SSL был заменен протоколом TLS, который является его преемником и предлагает улучшенную безопасность. Текущие версии TLS (1.2 и 1.3) используются вместо SSL.
🟠Основные отличия:
TLS обеспечивает более сильное шифрование, лучшее управление сессионными ключами и устранение уязвимостей, найденных в SSL.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Какие преимущества у горутин над тредами?

1. Легковесность – горутины потребляют меньше памяти, чем системные потоки.
2. Планировщик Go – позволяет управлять миллионами горутин без явного управления потоками.
3. Простота работы с конкурентностью – нет необходимости использовать mutex для синхронизации, благодаря каналам.
4. Низкие затраты на переключение контекста – переключение между горутинами быстрее, чем между потоками.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Зачем синхронизировать доступ данных?

В многопоточных (параллельных) программах горутины (goroutines) могут одновременно изменять одни и те же данные. Если не синхронизировать доступ, это приведёт к гонке данных (data race), когда несколько потоков читают/пишут одно и то же значение одновременно.

🚩`sync.Mutex` (мьютексы — блокировка данных)

Используется для блокировки критической секции кода, чтобы в один момент только одна горутина могла изменять данные.
package main

import (
"fmt"
"sync"
)

var (
counter int
mutex sync.Mutex
)

func increment(wg *sync.WaitGroup) {
defer wg.Done()
mutex.Lock() // Блокируем доступ
counter++ // Изменяем данные
mutex.Unlock() // Разблокируем доступ
}

func main() {
var wg sync.WaitGroup

for i := 0; i < 1000; i++ {
wg.Add(1)
go increment(&wg)
}

wg.Wait()
fmt.Println("Итоговый счетчик:", counter) // 1000
}


🚩`sync.RWMutex` (разделяемая блокировка)

Позволяет нескольким горутинам читать данные одновременно, но блокирует запись.
var (
data int
mutex sync.RWMutex
)

func readData(wg *sync.WaitGroup) {
defer wg.Done()
mutex.RLock() // Разрешаем чтение
fmt.Println("Читаем данные:", data)
mutex.RUnlock()
}

func writeData(wg *sync.WaitGroup) {
defer wg.Done()
mutex.Lock() // Блокируем на запись
data++
mutex.Unlock()
}


🚩`sync.WaitGroup` (ожидание горутин)

Позволяет дождаться завершения всех горутин без блокировки данных.
var wg sync.WaitGroup

wg.Add(2) // Ожидаем 2 горутины
go func() {
defer wg.Done()
fmt.Println("Горутина 1 завершилась")
}()
go func() {
defer wg.Done()
fmt.Println("Горутина 2 завершилась")
}()

wg.Wait() // Ждём завершения всех горутин
fmt.Println("Все горутины завершены")


🚩`sync/atomic` (атомарные операции)

Атомарные операции быстрее мьютексов и гарантируют безопасное обновление переменных без гонок данных.
import "sync/atomic"

var counter int64

func incrementAtomic() {
atomic.AddInt64(&counter, 1) // Атомарное увеличение
}


🚩Каналы (лучший способ в Go)

В Go рекомендуется избегать блокировок и использовать каналы для передачи данных между горутинами.
package main

import "fmt"

func main() {
ch := make(chan int) // Канал для передачи данных

go func() {
ch <- 42 // Отправляем данные
}()

data := <-ch // Получаем данные
fmt.Println("Получено:", data)
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Какие есть недостатки у горутин относительно тредов?

1. Отсутствие привязки к конкретному ядру – горутины исполняются планировщиком Go, а не напрямую ОС.
2. Необходимость контроля за утечками – забытые горутины продолжают выполняться, потребляя ресурсы.
3. Ограниченная поддержка системных вызовов – при блокирующих операциях Go может выделять новый поток ОС.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Как слайсы работают?

Слайс (slice) — это динамический массив, который ссылается на часть массива в памяти. В отличие от массивов (array), слайсы могут изменять размер.

🚩Внутреннее устройство слайса

Слайс в Go — это структура
type SliceHeader struct {
Data uintptr // Указатель на массив в памяти
Len int // Длина слайса (количество элементов)
Cap int // Вместимость (capacity) — сколько элементов может вместить без перевыделения памяти
}


Пример структуры слайса
arr := [5]int{1, 2, 3, 4, 5}
s := arr[1:4] // Берём срез от 2-го до 4-го элемента
fmt.Println(s) // [2 3 4]


🚩Создание слайсов

Есть несколько способов создать слайс:
Способ 1: Срез массива
arr := [5]int{10, 20, 30, 40, 50}
s := arr[1:4] // [20 30 40]


Способ 2: Использование make()
s := make([]int, 3, 5) // Длина 3, вместимость 5
fmt.Println(s, len(s), cap(s)) // [0 0 0] 3 5


Способ 3: Литерал (инициализация значениями)
s := []int{1, 2, 3}
fmt.Println(s) // [1 2 3]


🚩Изменение слайса

Слайсы можно изменять, используя append().
s := []int{1, 2, 3}
s = append(s, 4, 5)
fmt.Println(s) // [1 2 3 4 5]


🚩Как растёт `slice`?

Когда append() увеличивает slice, Go использует оптимизированный алгоритм роста:
- Если cap < 1024, слайс удваивает размер (cap *= 2).
- Если cap >= 1024, рост идёт примерно на 25% (cap += cap / 4).
s := []int{}
for i := 0; i < 10; i++ {
s = append(s, i)
fmt.Printf("Len: %d, Cap: %d\n", len(s), cap(s))
}


Выход (пример)
Len: 1, Cap: 1
Len: 2, Cap: 2
Len: 3, Cap: 4
Len: 5, Cap: 8
Len: 9, Cap: 16


🚩Как избежать неожиданных эффектов?

Так как слайсы хранят ссылку на массив, возможны побочные эффекты.
arr := [5]int{1, 2, 3, 4, 5}
s1 := arr[:3] // [1 2 3]
s2 := arr[2:] // [3 4 5]
s2[0] = 100 // Меняем первый элемент s2

fmt.Println(s1) // [1 2 100] ❗️ s1 тоже изменился


Решение: используйте copy() для создания нового массива.
s1 := []int{1, 2, 3}
s2 := make([]int, len(s1))
copy(s2, s1) // Копируем данные
s2[0] = 100
fmt.Println(s1) // [1 2 3] Оригинал не изменился


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Что можно делать с закрытым каналом?

- Читать данные, пока буфер не станет пустым.
- Использовать range, чтобы получить оставшиеся элементы.
- Проверять закрытие с помощью второго параметра (ok).
Записывать в закрытый канал нельзя – это приведет к panic.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Как устроены контексты в Go?

Контексты (context.Context) представляют собой интерфейс, который используется для передачи мета-данных, управления сроками действия и отменой операций в иерархии вызовов функций. Основная цель контекста — обеспечение способа для остановки выполнения программы (например, запросов или подпроцессов) по требованию. Это особенно полезно в сетевых приложениях, где вам может потребоваться прервать выполнение операции, которая больше не требуется или занимает слишком много времени.

🚩Ключевые особенности

🟠Пропаганда по горутинам
Контекст предназначен для безопасной передачи по горутинам, что означает, что он может быть передан между различными частями вашей программы, выполняющимися в разных горутинах.

🟠Отмена операций
Контекст может быть отменен, что автоматически сигнализирует всем получателям этого контекста о необходимости остановить свою работу. Это обеспечивается через канал, который закрывается, когда контекст отменяется.

🟠Сроки выполнения
Контекст может иметь установленный таймаут или дедлайн, после достижения которого он автоматически отменяется.

🟠Значения
Контексты могут нести значения — пары ключ-значение, которые можно устанавливать и получать. Эти значения обычно используются для передачи данных, специфичных для запроса, таких как идентификаторы сессий или токены авторизации.

🚩Создание и использование контекста

🟠context.Background()
Возвращает пустой контекст, который никогда не отменяется. Обычно используется в основной функции и при инициализации.

🟠context.TODO()
Используется для указания, что должен быть предоставлен подходящий контекст. Обычно применяется в разработке и при рефакторинге.

🟠context.WithCancel(parent Context) (ctx Context, cancel CancelFunc)
Создает новый контекст с возможностью отмены.

🟠context.WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)
Создает контекст, который автоматически отменяется в указанный deadline.

🟠context.WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
Аналогичен WithDeadline, но устанавливает время жизни контекста на основе заданного таймаута.

func operation1(ctx context.Context) {
select {
case <-time.After(5 * time.Second):
fmt.Println("operation1 completed")
case <-ctx.Done():
fmt.Println("operation1 cancelled")
}
}

func main() {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()

go operation1(ctx)

// Дожидаемся завершения или отмены операции
<-ctx.Done()
if err := ctx.Err(); err != nil {
fmt.Println("main:", err)
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Что делать, если канал интовый и был получен ноль?

Проверить второй параметр при чтении:
- Если ok == false – канал закрыт, а 0 не является значением.
- Если ok == true – это действительное значение 0, переданное в канал.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Какие преимущества есть у Go?

Go (или Golang) обладает рядом преимуществ, которые делают его популярным.

🟠Простота и лаконичность
Был разработан с упором на простоту синтаксиса и лаконичность. Это делает язык легким для изучения и чтения кода. Минимализм языка позволяет разработчикам сосредоточиться на решении задач, а не на сложностях синтаксиса.

🟠Высокая производительность
Компилируется в машинный код, что обеспечивает высокую производительность выполнения программ. Производительность Go сопоставима с производительностью программ, написанных на C или C++, благодаря низкоуровневой оптимизации компилятора.

🟠Параллелизм и конкурентность
Одним из ключевых преимуществ является встроенная поддержка параллелизма и конкурентности. С помощью горутин и каналов разработчики могут легко создавать многопоточные приложения.

package main

import (
"fmt"
"time"
)

func say(s string) {
for i := 0; i < 3; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}

func main() {
go say("world")
say("hello")
}


🟠Сильная система типов и безопасность памяти
Имеет строгую систему типов, которая помогает предотвращать ошибки на этапе компиляции. В языке отсутствуют неявные преобразования типов, что снижает вероятность ошибок. Также Go управляет памятью с помощью встроенного сборщика мусора (garbage collector), что предотвращает утечки памяти.

🟠Встроенная поддержка
Стандартных инструментов
Поставляется с богатым набором встроенных инструментов для разработки, таких как:
go fmt для автоматического форматирования кода.
go test для запуска тестов.
go build и go run для сборки и выполнения программ.
go doc для генерации документации.

🟠Кроссплатформенность
Поддерживает компиляцию кода для различных платформ и операционных систем. Это делает его удобным для разработки кроссплатформенных приложений.

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

🟠Сообщество и поддержка
Имеет большое и активное сообщество, а также поддерживается компанией Google. Это гарантирует наличие множества ресурсов для обучения и решения возникающих вопросов, а также активное развитие и улучшение языка.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Через какой канал возможна проверка работы горутины?

Можно использовать канал done, который закроется или передаст значение при завершении работы горутины. Например, через select можно отслеживать завершение.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Чем stream отличается от unary?

В контексте gRPC (Google Remote Procedure Call) unary и stream — это два разных типа взаимодействия между клиентом и сервером.

🚩Unary RPC (Обычный вызов)

🟠Unary (унарный) вызов
это стандартный запрос-ответ:
Клиент отправляет одно сообщение → сервер отвечает одним сообщением.
service UserService {
rpc GetUserInfo(UserRequest) returns (UserResponse);
}


Go-реализация Unary RPC
func (s *server) GetUserInfo(ctx context.Context, req *pb.UserRequest) (*pb.UserResponse, error) {
user := &pb.UserResponse{
Id: req.Id,
Name: "John Doe",
Email: "john@example.com",
}
return user, nil // Обычный ответ
}


🚩Streaming RPC (Потоковый вызов)

В streaming RPC передача данных идёт потоком. gRPC поддерживает три вида стримов:

🟠Server Streaming (Поток от сервера)
Клиент отправляет один запрос → сервер возвращает поток ответов.
service UserService {
rpc GetUserActivity(UserRequest) returns (stream ActivityResponse);
}


Go-реализация Server Streaming
func (s *server) GetUserActivity(req *pb.UserRequest, stream pb.UserService_GetUserActivityServer) error {
activities := []string{"Login", "Upload File", "Logout"}
for _, activity := range activities {
err := stream.Send(&pb.ActivityResponse{Message: activity})
if err != nil {
return err
}
time.Sleep(time.Second) // Имитация задержки
}
return nil
}


🟠Client Streaming (Поток от клиента)
Клиент отправляет поток данных → сервер отвечает одним ответом.
service UploadService {
rpc UploadFile(stream FileChunk) returns (UploadResponse);
}


Go-реализация Client Streaming
func (s *server) UploadFile(stream pb.UploadService_UploadFileServer) error {
var totalSize int64
for {
chunk, err := stream.Recv()
if err == io.EOF {
return stream.SendAndClose(&pb.UploadResponse{Size: totalSize})
}
if err != nil {
return err
}
totalSize += chunk.Size
}
}


🟠Bidirectional Streaming (Двунаправленный поток)
Клиент и сервер обмениваются данными в потоке одновременно.
service ChatService {
rpc Chat(stream ChatMessage) returns (stream ChatMessage);
}


Go-реализация Bi-directional Streaming
func (s *server) Chat(stream pb.ChatService_ChatServer) error {
for {
msg, err := stream.Recv()
if err == io.EOF {
return nil
}
if err != nil {
return err
}
response := &pb.ChatMessage{Text: "Echo: " + msg.Text}
if err := stream.Send(response); err != nil {
return err
}
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Как закрыть канал?

Использовать close(channel). После этого:
- Новые записи приведут к panic.
- Чтение продолжится, пока не будет пустым.
- Чтение из пустого закрытого канала даст zero-value.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Что такое интеграционные тесты?

Интеграционные тесты — это тесты, которые проверяют взаимодействие между различными компонентами системы. В отличие от юнит-тестов, которые фокусируются на проверке отдельных функций или методов, интеграционные проверяют, как компоненты работают вместе, обеспечивая целостность и корректность всей системы.

🚩Зачем они нужны?

🟠Проверка взаимодействия компонентов
Интеграционные тесты помогают убедиться, что различные части системы правильно взаимодействуют друг с другом. Это важно для выявления проблем на границах между компонентами, которые могут не проявиться в юнит-тестах.

🟠Выявление ошибок интеграции
Такие тесты помогают обнаруживать ошибки, возникающие при объединении модулей. Это могут быть проблемы с форматами данных, неправильное использование интерфейсов и другие ошибки, связанные с интеграцией.

🟠Проверка реальных сценариев использования
Интеграционные тесты могут включать сценарии, приближенные к реальным условиям эксплуатации системы, что позволяет убедиться, что система работает правильно в реальной среде.

🚩Как их писать?

Интеграционные тесты могут требовать наличия нескольких зависимостей, таких как база данных, внешние API или другие сервисы. Настройте тестовую среду, которая будет эмулировать реальную среду.
      // main.go
package main

import (
"database/sql"
_ "github.com/mattn/go-sqlite3"
"log"
)

type User struct {
ID int
Name string
}

func CreateUser(db *sql.DB, name string) (int, error) {
res, err := db.Exec("INSERT INTO users(name) VALUES(?)", name)
if err != nil {
return 0, err
}
id, err := res.LastInsertId()
if err != nil {
return 0, err
}
return int(id), nil
}

func GetUser(db *sql.DB, id int) (User, error) {
var user User
err := db.QueryRow("SELECT id, name FROM users WHERE id = ?", id).Scan(&user.ID, &user.Name)
if err != nil {
return user, err
}
return user, nil
}

func main() {
db, err := sql.Open("sqlite3", ":memory:")
if err != nil {
log.Fatal(err)
}
defer db.Close()

_, err = db.Exec("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)")
if err != nil {
log.Fatal(err)
}
}
// main_test.go
package main

import (
"database/sql"
"testing"
_ "github.com/mattn/go-sqlite3"
)

func setupTestDB(t *testing.T) *sql.DB {
db, err := sql.Open("sqlite3", ":memory:")
if err != nil {
t.Fatal(err)
}
_, err = db.Exec("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)")
if err != nil {
t.Fatal(err)
}
return db
}

func TestCreateAndGetUser(t *testing.T) {
db := setupTestDB(t)
defer db.Close()

// Создание пользователя
userID, err := CreateUser(db, "John Doe")
if err != nil {
t.Fatalf("Failed to create user: %v", err)
}

// Получение пользователя
user, err := GetUser(db, userID)
if err != nil {
t.Fatalf("Failed to get user: %v", err)
}

// Проверка результатов
if user.Name != "John Doe" {
t.Errorf("Expected name to be 'John Doe', got %s", user.Name)
}
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Какие действия можно производить с каналами?

1. Отправлять данные (chan <- value).
2. Читать данные (value := <-chan).
3. Закрывать (close(chan)).
4. Использовать в select для обработки множественных каналов.
5. Создавать буферизированные и небуферизированные каналы.
6. Использовать range для чтения оставшихся данных из закрытого канала.


Ставь 👍 если знал ответ, 🔥 если нет
Забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM