Golang | Вопросы собесов
4.33K subscribers
27 photos
694 links
Download Telegram
🤔 Какое ключевое слово используется для объявления неименованного канала в Go?
Anonymous Quiz
7%
channel
72%
chan
15%
make
6%
new
Что такое указатели ?
Спросят с вероятностью 8%

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

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

1️⃣Эффективность:
Указатели позволяют передавать большие структуры данных в функции без необходимости копирования. Вместо этого передается адрес структуры, что экономит память и время выполнения.

2️⃣Модификация данных:
Указатели позволяют функциям изменять значения переменных, передаваемых им в качестве аргументов. Это позволяет создавать более гибкие и мощные функции.

3️⃣Создание сложных структур данных:
Указатели используются для создания ссылочных типов данных, таких как связные списки, деревья и графы.

Как их использовать?

1️⃣Создание и использование:
      package main

import "fmt"

func main() {
var x int = 10
var p *int // Объявление указателя на int
p = &x // Присваивание указателю адреса переменной x

fmt.Println("Value of x:", x) // Output: 10
fmt.Println("Address of x:", p) // Вывод адреса x
fmt.Println("Value via p:", *p) // Разыменование указателя, Output: 10
}

Здесь переменная p является указателем на int и хранит адрес переменной x. Разыменование указателя (*p) позволяет получить значение, хранящееся по этому адресу.

2️⃣Функции:
      package main

import "fmt"

func main() {
x := 10
fmt.Println("Before:", x) // Output: 10

increment(&x)
fmt.Println("After:", x) // Output: 11
}

func increment(p *int) {
*p++ // Увеличение значения по адресу, на который указывает p
}

В данном примере функция increment принимает указатель на int и увеличивает значение по этому адресу. Это позволяет изменять значение переменной x непосредственно.

3️⃣Использование со структурами:
      package main

import "fmt"

type Person struct {
name string
age int
}

func main() {
p := Person{name: "Alice", age: 30}
fmt.Println("Before:", p) // Output: {Alice 30}

updatePerson(&p)
fmt.Println("After:", p) // Output: {Alice 31}
}

func updatePerson(p *Person) {
p.age++ // Изменение поля структуры через указатель
}

В этом примере функция updatePerson изменяет поле структуры Person через указатель на эту структуру.

Преимущества:
Эффективность работы с памятью: Указатели позволяют передавать данные без копирования, что улучшает производительность.
Гибкость в изменении данных: Указатели позволяют функциям изменять переданные им данные.
Поддержка сложных структур данных: Указатели необходимы для создания динамических структур данных.

Недостатки:
Сложность управления памятью: Неправильное использование указателей может привести к утечкам памяти и ошибкам, таким как разыменование нулевого указателя.
Усложнение кода: Работа с указателями может сделать код менее читаемым и трудным для отладки.

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

👉 Можно посмотреть Примеры как отвечают люди на этот вопрос, или перейти К списку 349 вопроса на Golang разработчика. Ставь 👍 если нравится контент

🔐 База собесов | 🔐 База тестовых
🤔 Какой тип должен реализовывать метод Error() string?
Anonymous Quiz
57%
error
7%
fmt.Stringer
11%
string
26%
fmt.Error
Что будет в Map, если не делать make или short assign ?
Спросят с вероятностью 8%

Карты (maps) необходимо инициализировать перед использованием. Если карта не была инициализирована с помощью функции make или литерала карты, она будет иметь нулевое значение nil. Попытка добавить элементы в такую карту приведет к панике (panic).

Пример с неинициализированной картой
package main

import "fmt"

func main() {
var myMap map[string]int
// Карта не инициализирована, её значение - nil
fmt.Println(myMap) // Output: map[]

// Попытка добавить элемент в неинициализированную карту
// Это вызовет панику
myMap["key"] = 1
}


Этот код приведет к панике с сообщением об ошибке:
panic: assignment to entry in nil map


Инициализация карты

Чтобы избежать паники, карту необходимо инициализировать перед использованием. Есть 2 способа:

1️⃣Использование функции `make`:
      package main

import "fmt"

func main() {
// Инициализация карты с помощью make
myMap := make(map[string]int)
myMap["key"] = 1
fmt.Println(myMap) // Output: map[key:1]
}


2️⃣Использование литерала карты:
      package main

import "fmt"

func main() {
// Инициализация карты с помощью литерала карты
myMap := map[string]int{
"key1": 1,
"key2": 2,
}
fmt.Println(myMap) // Output: map[key1:1 key2:2]
}


Пример:
package main

import "fmt"

func main() {
// Инициализация карты с помощью make
myMap := make(map[string]int)

// Добавление элементов в карту
myMap["one"] = 1
myMap["two"] = 2

// Вывод карты
fmt.Println(myMap) // Output: map[one:1 two:2]

// Доступ к элементу карты
value := myMap["one"]
fmt.Println("Value for key 'one':", value) // Output: Value for key 'one': 1

// Проверка наличия ключа в карте
if val, exists := myMap["three"]; exists {
fmt.Println("Key 'three' exists with value:", val)
} else {
fmt.Println("Key 'three' does not exist")
}

// Удаление элемента из карты
delete(myMap, "two")
fmt.Println(myMap) // Output: map[one:1]
}


Если карта не была инициализирована с помощью make или литерала карты, она будет nil. Попытка добавить элементы в такую карту приведет к панике. Чтобы избежать этого, необходимо инициализировать карту перед использованием.

👉 Можно посмотреть Примеры как отвечают люди на этот вопрос, или перейти К списку 349 вопроса на Golang разработчика. Ставь 👍 если нравится контент

🔐 База собесов | 🔐 База тестовых
📌 Как проводить тестирования в Go ?

💬 Спрашивают в 8% собеседований

Встроены мощные инструменты для написания и запуска тестов. Тестирование — важная часть разработки ПО, так как помогает убедиться, что код работает правильно и что изменения не приводят к регрессиям.

Основные компоненты

1️⃣ Создание тестовых файлов:

Тестовые файлы в Go должны оканчиваться на _test.go. Это позволяет инструменту тестирования Go автоматически распознавать их как тесты.
Тестовые функции должны начинаться с Test и принимать один аргумент типа *testing.T.

2️⃣ Написание простого теста:

Рассмотрим простой пример, в котором тестируется функция, добавляющая два числа:
      // main.go
package main

func Add(a, b int) int {
return a + b
}
// main_test.go
package main

import "testing"

func TestAdd(t *testing.T) {
result := Add(2, 3)
expected := 5
if result != expected {
t.Errorf("Add(2, 3) = %d; want %d", result, expected)
}
}


3️⃣ Запуск тестов:

Чтобы запустить тесты, используйте команду go test в терминале. Она автоматически найдет и выполнит все тесты в текущем пакете.
      $ go test


4️⃣ Тестирование с таблицей (table-driven tests):

Table-driven tests — это подход, при котором вы пишете тесты в виде таблицы входных и ожидаемых значений. Это позволяет легко добавлять новые тестовые случаи и улучшает читаемость.
      // main_test.go
package main

import "testing"

func TestAdd(t *testing.T) {
tests := []struct {
a, b, expected int
}{
{1, 2, 3},
{0, 0, 0},
{-1, -1, -2},
{2, 2, 4},
}

for _, tt := range tests {
result := Add(tt.a, tt.b)
if result != tt.expected {
t.Errorf("Add(%d, %d) = %d; want %d", tt.a, tt.b, result, tt.expected)
}
}
}


🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Какой интерфейс используется для записи данных в различные источники?
Anonymous Quiz
92%
io.Writer
4%
io.Reader
2%
io.Output
2%
io.Printer
📌 Какое свойство должно быть у ключа в Map ?

💬 Спросят с вероятностью 8%

Карты (maps) должны быть сравнимыми. Это означает, что типы, используемые в качестве ключей, должны поддерживать операции сравнения на равенство (==) и неравенство (!=). Сравнимость ключей необходима для корректного функционирования карт, так как операции поиска, вставки и удаления требуют проверки на равенство ключей.

🤔 Типы данных, которые могут быть ключами

1️⃣ Целые числа (int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64):
      var intMap map[int]string
intMap = make(map[int]string)
intMap[1] = "one"

2️⃣ Строки (string):
      var stringMap map[string]int
stringMap = make(map[string]int)
stringMap["one"] = 1

3️⃣ Булевы значения (bool):
      var boolMap map[bool]string
boolMap = make(map[bool]string)
boolMap[true] = "yes"
boolMap[false] = "no"

4️⃣ Указатели (например, *int, *struct):
      var ptrMap map[*int]string
ptrMap = make(map[*int]string)
var a int
ptrMap[&a] = "pointer to a"

5️⃣ Каналы (chan T):
      var chanMap map[chan int]string
chanMap = make(map[chan int]string)
c := make(chan int)
chanMap[c] = "channel"


6️⃣ Интерфейсы (при условии, что их конкретные значения сравнимы):
      var ifaceMap map[interface{}]string
ifaceMap = make(map[interface{}]string)
ifaceMap[1] = "one"
ifaceMap["key"] = "value"

7️⃣ Структуры (struct), если все их поля сравнимы:
      type Person struct {
Name string
Age int
}
var structMap map[Person]string
structMap = make(map[Person]string)
structMap[Person{"Alice", 30}] = "person 1"


🤔 Типы данных, которые не могут быть ключами

1️⃣ Срезы (slice):
      // Это вызовет ошибку компиляции
// var sliceMap map[[]int]string


2️⃣ Карты (map):
      // Это вызовет ошибку компиляции
// var mapMap map[map[int]string]string

3️⃣ Функции (func):
      // Это вызовет ошибку компиляции
// var funcMap map[func() int]string

🤔 Пример:
package main

import "fmt"

type Person struct {
Name string
Age int
}

func main() {
// Карта с ключами типа int
intMap := make(map[int]string)
intMap[1] = "one"
intMap[2] = "two"
fmt.Println(intMap) // Output: map[1:one 2:two]

// Карта с ключами типа string
stringMap := make(map[string]int)
stringMap["one"] = 1
stringMap["two"] = 2
fmt.Println(stringMap) // Output: map[one:1 two:2]

// Карта с ключами типа bool
boolMap := make(map[bool]string)
boolMap[true] = "yes"
boolMap[false] = "no"
fmt.Println(boolMap) // Output: map[false:no true:yes]

// Карта с ключами типа *int
a, b := 1, 2
ptrMap := make(map[*int]string)
ptrMap[&a] = "pointer to a"
ptrMap[&b] = "pointer to b"
fmt.Println(ptrMap) // Output: map[0x...:pointer to a 0x...:pointer to b]

// Карта с ключами типа struct
structMap := make(map[Person]string)
structMap[Person{"Alice", 30}] = "person 1"
structMap[Person{"Bob", 25}] = "person 2"
fmt.Println(structMap) // Output: map[{Alice 30}:person 1 {Bob 25}:person 2]
}


Ключи должны быть сравнимыми, то есть поддерживать операции сравнения (== и !=). Это включает целые числа, строки, булевы значения, указатели, каналы, интерфейсы (сравнимые) и структуры (если все их поля сравнимы). Срезы, карты и функции не могут быть использованы в качестве ключей.

🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Какой метод используется для декодирования JSON данных в Go?
Anonymous Quiz
82%
Unmarshal
12%
Decode
5%
Parse
2%
Deserialize
📌 Что такое unit тесты ?

💬 Спрашивают в 8% собеседований

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

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

1️⃣ Обнаружение ошибок на ранней стадии:
Юнит-тесты помогают выявлять ошибки и проблемы в коде на самых ранних этапах разработки, что снижает затраты на их исправление.

2️⃣Документация кода:
Хорошо написанные юнит-тесты служат живой документацией для кода, показывая, как именно должны работать функции и методы.

3️⃣Поддержка рефакторинга:
Наличие юнит-тестов делает процесс рефакторинга безопаснее, так как тесты помогают убедиться, что изменения в коде не нарушили его корректную работу.

4️⃣Повышение уверенности в коде:
Юнит-тесты обеспечивают уверенность в том, что код работает правильно, что особенно важно при добавлении новых функций или исправлении ошибок.

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

1️⃣Создание тестовых файлов:
Тестовые файлы в Go должны оканчиваться на _test.go.
Тестовые функции должны начинаться с Test и принимать один аргумент типа *testing.T.

2️⃣Пример написания юнит-теста:
Рассмотрим пример, где у нас есть функция, которая складывает два числа, и мы пишем для неё юнит-тест.
      // main.go
package main

func Add(a, b int) int {
return a + b
}
// main_test.go
package main

import "testing"

func TestAdd(t *testing.T) {
result := Add(2, 3)
expected := 5
if result != expected {
t.Errorf("Add(2, 3) = %d; want %d", result, expected)
}
}


3️⃣Запуск юнит-тестов:
Чтобы запустить юнит-тесты, используйте команду go test в терминале.
      $ go test


4️⃣Тестирование с таблицей (table-driven tests):
Table-driven tests позволяют легко добавлять новые тестовые случаи и улучшать читаемость тестов.
      package main

import "testing"

func TestAdd(t *testing.T) {
tests := []struct {
a, b, expected int
}{
{1, 2, 3},
{0, 0, 0},
{-1, -1, -2},
{2, 2, 4},
}

for _, tt := range tests {
result := Add(tt.a, tt.b)
if result != tt.expected {
t.Errorf("Add(%d, %d) = %d; want %d", tt.a, tt.b, result, tt.expected)
}
}
}


Лучшие практики

1️⃣Соблюдайте атомарность:
Каждый юнит-тест должен проверять только одну конкретную функцию или метод и проверять один аспект её поведения.

2️⃣Используйте понятные названия:
Имена тестовых функций должны быть понятными и отражать их цель. Например, TestAdd для тестирования функции Add.

3️⃣Проверяйте крайние случаи:
Убедитесь, что ваши тесты проверяют крайние случаи и потенциальные ошибки, такие как нулевые значения, отрицательные числа и переполнения.

4️⃣Регулярно запускайте тесты:
Регулярно запускайте тесты, особенно перед коммитами и релизами, чтобы убедиться в отсутствии регрессий.

5️⃣Используйте mocking, если необходимо:
При тестировании функций, которые зависят от внешних ресурсов (таких как базы данных или API), используйте мокирование (mocking) для изоляции тестов.

Юнит-тесты проверяют отдельные функции или методы на корректность их работы. Они помогают выявлять ошибки на ранней стадии, служат документацией и поддерживают безопасный рефакторинг кода. Пишутся в файлах с суффиксом _test.go и запускаются с помощью команды go test.

🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Какой метод используется для чтения содержимого файла в Go?
Anonymous Quiz
44%
Read
38%
ReadFile
17%
Open
1%
Load
📌 Как работает append в слайсе ?

💬 Спрашивают в 8% собеседований

Срезы (slices) — это динамические массивы, которые могут изменять свой размер. Встроенная функция append используется для добавления новых элементов к срезу.
Основные особенности:

1️⃣Динамическое расширение:
Могут увеличивать свой размер динамически. Когда вызывается функция append, если подлежащий массив не имеет достаточной емкости для размещения новых элементов, создается новый массив, и все существующие элементы копируются в него.

2️⃣Изменение длины и емкости:
Функция append изменяет длину среза, добавляя новые элементы. Емкость может также увеличиваться, если требуется больше памяти для новых элементов.

3️⃣Возвращаемое значение:
append возвращает новый срез, который включает добавленные элементы. Этот новый срез может указывать на новый массив, если емкость исходного была недостаточной.

Пример:
package main

import "fmt"

func main() {
// Создание пустого среза
var s []int

// Добавление одного элемента
s = append(s, 1)
fmt.Println(s) // Output: [1]

// Добавление нескольких элементов
s = append(s, 2, 3, 4)
fmt.Println(s) // Output: [1 2 3 4]

// Добавление элементов из другого среза
t := []int{5, 6, 7}
s = append(s, t...)
fmt.Println(s) // Output: [1 2 3 4 5 6 7]
}


🤔 Когда вызывается функция append, происходит следующее:

1️⃣Проверка емкости:
Проверяется, достаточно ли емкости в подлежащем массиве для размещения новых элементов. Емкость (capacity) среза — это размер подлежащего массива, который может быть больше, чем текущая длина (length) среза.

2️⃣Выделение нового массива:
Если емкости недостаточно, выделяется новый массив, обычно в два раза больший, чем текущий размер.

3️⃣Копирование данных:
Все существующие элементы копируются в новый массив, и новые элементы добавляются в конец.

4️⃣Возврат нового среза:
Новый срез, указывающий на новый массив, возвращается.

Пример:
package main

import "fmt"

func main() {
// Изначальный срез с длиной 0 и емкостью 0
s := []int{}
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)

// Добавление элементов и отслеживание изменений длины и емкости
for i := 1; i <= 10; i++ {
s = append(s, i)
fmt.Printf("len=%d cap=%d %v\n", len(s), cap(s), s)
}
}


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

Функция append добавляет новые элементы к срезу, изменяя его длину. Если емкости подлежащего массива недостаточно, создается новый массив, в который копируются все элементы. Она возвращает новый срез, который может указывать на новый массив, если емкость была увеличена.

🔥 ТОП ВОПРОСОВ С СОБЕСОВ

🔒 База собесов | 🔒 База тестовых
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Какой пакет используется для работы с регулярными выражениями в Go?
Anonymous Quiz
42%
regexp
38%
regex
10%
re
10%
reg
📌 Что такое интеграционные тесты ?

💬 Спрашивают в 8% собеседований

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

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

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

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

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

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

1️⃣Создание тестовой среды:
Интеграционные тесты могут требовать наличия нескольких зависимостей, таких как база данных, внешние API или другие сервисы. Настройте тестовую среду, которая будет эмулировать реальную среду.

2️⃣Пример:
Рассмотрим пример интеграционного теста для приложения, которое работает с базой данных. В этом примере мы будем тестировать функции создания и получения пользователя.
      // 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
🤔 Какое ключевое слово в Go используется для создания именованных типов, которые могут быть основой для методов?
Anonymous Quiz
6%
new
59%
type
21%
struct
14%
interface
Что будет, если подставить переменную в массив ?
Спросят с вероятностью 8%

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

Пример использования:
package main

import "fmt"

func main() {
// Объявление и инициализация массива
var arr [5]int

// Переменная, значение которой будет подставлено в массив
value := 10

// Подставляем значение переменной в массив
arr[0] = value

// Вывод массива
fmt.Println(arr) // Output: [10 0 0 0 0]
}


В этом примере:

1️⃣Мы объявляем массив arr из 5 целых чисел (int), изначально инициализированных нулями.
2️⃣Создаем переменную value и присваиваем ей значение 10.
3️⃣Подставляем значение переменной value в первый элемент массива arr с индексом 0.
4️⃣Выводим массив, чтобы увидеть результат.

Изменение элементов

Массивы индексируются с нуля, и доступ к элементам массива осуществляется с использованием квадратных скобок []. Можно изменять значения элементов массива, присваивая им новые значения через соответствующие индексы:
package main

import "fmt"

func main() {
// Объявление и инициализация массива
arr := [5]int{1, 2, 3, 4, 5}

// Переменные для подстановки
value1 := 10
value2 := 20

// Подстановка значений переменных в массив
arr[1] = value1
arr[3] = value2

// Вывод массива
fmt.Println(arr) // Output: [1 10 3 20 5]
}


Переменные также могут использоваться для индексов массива:
package main

import "fmt"

func main() {
// Объявление и инициализация массива
arr := [5]int{1, 2, 3, 4, 5}

// Переменные для индексов
idx1 := 2
idx2 := 4

// Подстановка значений в массив по индексам
arr[idx1] = 30
arr[idx2] = 50

// Вывод массива
fmt.Println(arr) // Output: [1 2 30 4 50]
}


Массивы могут содержать структуры. Рассмотрим пример с массивом структур:
package main

import "fmt"

// Определение структуры
type Person struct {
Name string
Age int
}

func main() {
// Объявление массива структур
var people [3]Person

// Переменная структуры
p := Person{Name: "Alice", Age: 30}

// Подстановка структуры в массив
people[0] = p

// Прямое присвоение структуры в массив
people[1] = Person{Name: "Bob", Age: 25}
people[2] = Person{Name: "Charlie", Age: 35}

// Вывод массива структур
fmt.Println(people)
// Output: [{Alice 30} {Bob 25} {Charlie 35}]
}


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

👉 Можно посмотреть Примеры как отвечают люди на этот вопрос, или перейти К списку 349 вопроса на Golang разработчика. Ставь 👍 если нравится контент

🔐 База собесов | 🔐 База тестовых
🤔 Какой метод используется для сериализации структуры в JSON формат?
Anonymous Quiz
17%
Unmarshal
5%
Encode
72%
Marshal
6%
Serialize
🤔 Как в Go пишут unit тесты со стандартным пакетом testing ?

Стандартный пакет testing предоставляет инструменты для написания и выполнения юнит-тестов. Давайте рассмотрим, как писать юнит-тесты с использованием этого пакета.

🚩Основные компоненты

🟠Тестовые файлы: Должны иметь суффикс _test.go. Это позволяет инструменту тестирования Go автоматически распознавать их как тестовые файлы.
🟠Тестовые функции: Должны начинаться с Test и принимать один аргумент типа *testing.T. Это соглашение позволяет инструменту тестирования Go идентифицировать их как тестовые функции.

package main

func Add(a, b int) int {
return a + b
}
// main_test.go
package main

import "testing"

func TestAdd(t *testing.T) {
result := Add(2, 3)
expected := 5
if result != expected {
t.Errorf("Add(2, 3) = %d; want %d", result, expected)
}
}


Чтобы это сделать, выполните команду go test в терминале. Эта команда автоматически найдет и выполнит все тесты в текущем пакете: $ go test

Тестирование с таблицей (table-driven tests): Это подход, при котором вы пишете тесты в виде таблицы входных и ожидаемых значений. Это улучшает читаемость и позволяет легко добавлять новые тестовые случаи.
package main

import "testing"

func TestAdd(t *testing.T) {
tests := []struct {
a, b, expected int
}{
{1, 2, 3},
{0, 0, 0},
{-1, -1, -2},
{2, 2, 4},
}

for _, tt := range tests {
result := Add(tt.a, tt.b)
if result != tt.expected {
t.Errorf("Add(%d, %d) = %d; want %d", tt.a, tt.b, result, tt.expected)
}
}
}


Бенчмаркинг: Для измерения производительности кода используйте функции, начинающиеся с Benchmark и принимающие *testing.B.
package main

import "testing"

func BenchmarkAdd(b *testing.B) {
for i := 0; i < b.N; i++ {
Add(2, 3)
}
}


Примерные тесты (Example Tests)

package main

import "fmt"

func ExampleAdd() {
fmt.Println(Add(2, 3))
// Output: 5
}


🚩Лучшие практики написания данных тестов

🟠Соблюдайте атомарность: Каждый юнит-тест должен проверять только одну конкретную функцию или метод и проверять один аспект её поведения.
🟠Используйте понятные названия: Имена тестовых функций должны быть понятными и отражать их цель. Например, TestAdd для тестирования функции Add.
🟠Проверяйте крайние случаи: Убедитесь, что ваши тесты проверяют крайние случаи и потенциальные ошибки, такие как нулевые значения, отрицательные числа и переполнения.
🟠Регулярно запускайте тесты: Особенно перед коммитами и релизами, чтобы убедиться в отсутствии регрессий.
🟠Используйте mocking, если необходимо: При тестировании функций, которые зависят от внешних ресурсов (таких как базы данных или API), используйте мокирование (mocking) для изоляции тестов.

Запуск тестов
🟠Запуск всех тестов с подробным выводом: $ go test -v
🟠Запуск бенчмарков: $ go test -bench=.
🟠Покрытие кода: Чтобы увидеть покрытие тестами, используйте флаг -cover:$ go test -cover

Юнит-тесты пишутся в файлах с суффиксом _test.go и выполняются с помощью команды go test. Они проверяют отдельные функции или методы, чтобы убедиться, что они работают корректно. Пакет testing предоставляет все необходимые инструменты для написания и выполнения тестов, бенчмарков и примерных тестов.

Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Какое ключевое слово используется для импорта пакета в Go?
Anonymous Quiz
3%
include
2%
require
94%
import
1%
use
🤔 Как можно откопировать слайс ?

Для копирования срезов (slice) можно использовать встроенную функцию copy. Эта функция копирует элементы из одного среза в другой, обеспечивая корректное и эффективное копирование данных.


Принимает два аргумента: целевой срез и исходный срез. Она копирует элементы из исходного среза в целевой и возвращает количество скопированных элементов. В этом примере исходный срез src копируется в целевой срез dst. Размер целевого среза должен быть равен или больше размера исходного среза, чтобы все элементы могли быть скопированы.
package main

import "fmt"

func main() {
// Исходный срез
src := []int{1, 2, 3, 4, 5}

// Целевой срез, который должен иметь достаточно места для копирования элементов
dst := make([]int, len(src))

// Копирование среза
n := copy(dst, src)

// Вывод результатов
fmt.Println("Исходный срез:", src) // Output: Исходный срез: [1 2 3 4 5]
fmt.Println("Целевой срез:", dst) // Output: Целевой срез: [1 2 3 4 5]
fmt.Println("Количество скопированных элементов:", n) // Output: Количество скопированных элементов: 5
}


Частичное копирование: Если целевой срез меньше исходного, copy скопирует только столько элементов, сколько поместится в целевой срез.
package main

import "fmt"

func main() {
// Исходный срез
src := []int{1, 2, 3, 4, 5}

// Целевой срез меньшего размера
dst := make([]int, 3)

// Копирование среза
n := copy(dst, src)

// Вывод результатов
fmt.Println("Исходный срез:", src) // Output: Исходный срез: [1 2 3 4 5]
fmt.Println("Целевой срез:", dst) // Output: Целевой срез: [1 2 3]
fmt.Println("Количество скопированных элементов:", n) // Output: Количество скопированных элементов: 3
}


Копирование разных типов: Функция copy работает только с срезами одного типа. Если вы попытаетесь копировать элементы между срезами разных типов, это приведет к ошибке компиляции.
package main

import "fmt"

func main() {
// Исходный срез
src := []int{1, 2, 3, 4, 5}

// Целевой срез
dst := make([]int, 2)

// Копирование части среза
n := copy(dst, src[1:3])

// Вывод результатов
fmt.Println("Исходный срез:", src) // Output: Исходный срез: [1 2 3 4 5]
fmt.Println("Целевой срез:", dst) // Output: Целевой срез: [2 3]
fmt.Println("Количество скопированных элементов:", n) // Output: Количество скопированных элементов: 2
}


Глубокое копирование срезов с вложенными структурами: Если он содержит сложные структуры данных, такие как структуры или срезы с указателями, вам может понадобиться реализовать глубокое копирование, чтобы копировать не только сами элементы, но и данные, на которые они ссылаются.
package main

import "fmt"

type Item struct {
Value int
}

func main() {
// Исходный срез
src := []Item{{1}, {2}, {3}}

// Целевой срез
dst := make([]Item, len(src))

// Глубокое копирование
for i, item := range src {
dst[i] = Item{Value: item.Value}
}

// Изменение исходного среза не влияет на целевой срез
src[0].Value = 100

// Вывод результатов
fmt.Println("Исходный срез:", src) // Output: Исходный срез: [{100} {2} {3}]
fmt.Println("Целевой срез:", dst) // Output: Целевой срез: [{1} {2} {3}]
}


Ставь 👍 и забирай 📚 Базу знаний
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔 Какой тип используется для представления динамических массивов в Go?
Anonymous Quiz
6%
array
3%
list
86%
slice
6%
map
🤔 Что такое моки (mocks) ?

Моки (mocks) — это объекты, которые имитируют поведение реальных объектов в тестах. Они используются для изоляции тестируемого кода от зависимостей, таких как базы данных, внешние API, файловая система и другие сервисы. Моки позволяют сосредоточиться на тестировании конкретной части кода без необходимости задействовать реальные ресурсы, что делает тесты более быстрыми и предсказуемыми.

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

🟠Изоляция тестов: Позволяют изолировать тестируемый код от его зависимостей, что помогает убедиться, что тестируемая логика работает правильно сама по себе.
🟠Контроль над средой тестирования: С моками можно точно контролировать возвращаемые значения и поведение зависимостей, что позволяет создавать предсказуемые и повторяемые тесты.
🟠Ускорение тестов: Использование моков вместо реальных ресурсов (таких как базы данных или внешние API) делает тесты быстрее и снижает затраты на их выполнение.
🟠Тестирование ошибок и крайних случаев: Позволяют легко моделировать ошибки и крайние случаи, которые могут быть трудно воспроизвести с реальными зависимостями.

🚩Как их использовать моки ?

Для создания и использования их часто используют специальные библиотеки, такие как gomock или testify/mock. Рассмотрим пример использования библиотеки testify/mock.

Установка библиотеки testify
go get github.com/stretchr/testify


// api.go
package main

type APIClient interface {
FetchData(endpoint string) (string, error)
}

func GetData(client APIClient, endpoint string) (string, error) {
data, err := client.FetchData(endpoint)
if err != nil {
return "", err
}
return data, nil
}


Создадим мок для интерфейса APIClient с использованием библиотеки testify/mock.
// api_test.go
package main

import (
"errors"
"testing"

"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/assert"
)

// MockAPIClient is a mock type for the APIClient interface
type MockAPIClient struct {
mock.Mock
}

func (m *MockAPIClient) FetchData(endpoint string) (string, error) {
args := m.Called(endpoint)
return args.String(0), args.Error(1)
}

func TestGetData(t *testing.T) {
// Создание мока
mockClient := new(MockAPIClient)

// Настройка мока для возврата данных
mockClient.On("FetchData", "test-endpoint").Return("mocked data", nil)

// Вызов тестируемой функции
data, err := GetData(mockClient, "test-endpoint")

// Проверка результатов
assert.NoError(t, err)
assert.Equal(t, "mocked data", data)

// Проверка вызова мока
mockClient.AssertExpectations(t)
}

func TestGetDataWithError(t *testing.T) {
// Создание мока
mockClient := new(MockAPIClient)

// Настройка мока для возврата ошибки
mockClient.On("FetchData", "error-endpoint").Return("", errors.New("fetch error"))

// Вызов тестируемой функции
data, err := GetData(mockClient, "error-endpoint")

// Проверка результатов
assert.Error(t, err)
assert.Equal(t, "", data)

// Проверка вызова мока
mockClient.AssertExpectations(t)
}


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