Я пишу тесты для слоя repository с использованием реальной базы данных (<a href="https://testcontainers.com/" rel="nofollow">https://testcontainers.com/</a>) и столкнулся с проблемой при проверке структур, содержащих тип time.Time.
В качестве примера, вот результат одного из моих тестов:
Тест завершается с ошибкой:
<pre><code>Error: Not equal:
expected: &model.ColorSQL{ID:1, Name:"Cullen Gerhold", CreatedAt:time.Date(2025, time.March, 15, 20, 50, 54, 823874100, time.UTC), UpdatedAt:time.Date(2025, time.March, 15, 20, 50, 54, 823874100, time.UTC), DeletedAt:<nil>}
actual : &model.ColorSQL{ID:1, Name:"Cullen Gerhold", CreatedAt:time.Date(2025, time.March, 15, 20, 50, 54, 822076000, time.UTC), UpdatedAt:time.Date(2025, time.March, 15, 20, 50, 54, 822076000, time.UTC), DeletedAt:<nil>}</code></pre>
Первая мысль была в том, чтобы обновить код следующим образом:
Однако, если я решу обновить структуру и добавлю в нее новые поля, существует риск, что я забуду обновить соответствующие тесты. В результате, тесты могут пройти успешно, даже если новые поля не будут проверяться. Как можно избежать этой проблемы?
Проблемы с проверкой значений типа `time.Time` в тестах достаточно распространены, особенно когда дело касается точности времени, так как даже небольшие изменения могут привести к неравенству. Ваша ситуация показывает, что есть разница в наносекундах между ожидаемым и актуальным значением времени.
Вот несколько подходов, которые могут помочь вам решить эту проблему и избежать ошибок в будущем:
### 1. Используйте сравнение без учета наносекунд
Если для вашего случая важна только точность до секунд и потенциальные различия в наносекундах можно игнорировать, вы можете сравнивать `time.Time` значения, убирая наносекунды:
```go
func equalTimes(t1, t2 time.Time) bool {
return t1.Unix() == t2.Unix() && t1.Location() == t2.Location()
}
```
Затем в вашем тесте используйте эту функцию вместо прямого сравнения.
### 2. Определите структурированные методы сравнения
Если вам требуется более сложная логика для сравнения ваших структур, вы можете определить метод для сравнения полей:
```go
type ColorSQL struct {
ID int
Name string
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt *time.Time
}
func (c *ColorSQL) Equal(other *ColorSQL) bool {
return c.ID == other.ID &&
c.Name == other.Name &&
equalTimes(c.CreatedAt, other.CreatedAt) &&
equalTimes(c.UpdatedAt, other.UpdatedAt) &&
// Сравнение DeletedAt требует дополнительной логики
(c.DeletedAt == nil && other.DeletedAt == nil ||
(c.DeletedAt != nil && other.DeletedAt != nil && c.DeletedAt.Equal(*other.DeletedAt)))
}
```
Теперь вы можете использовать `c.Equal(&actualColor)` для проверки на равенство.
### 3. Стать педантичным в тестах
Другая рекомендация заключается в том, чтобы при разработке тестов обеспечивать, что вы проверяете все поля вашей структуры, добавляя больше подробностей в структуру вашего теста:
```go
if !expected.Equal(&actual) {
t.Errorf("Expected %v but got %v", expected, actual)
}
```
### 4. Mocking времени
Иногда полезно использовать библиотеку, которая позволяет "замокировать" текущее время. Одна из таких библиотек - `github.com/jonboulle/clockwork`, которая предоставляет интерфейс для работы с временем. Это может помочь вам избежать вероятностей, связанных с реальным временем.
### 5. Значения по умолчанию
Если вы тестируете структуру с временными метками, убедитесь, что значения, которые вы сравниваете, настройте так, чтобы они имели предсказуемые значения времени. Таким образом, вы уменьшаете вероятность различий, вызванных недостаточной точностью.
### Заключение
Подобные проблемы довольно типичны при тестировании, и важно использовать инструменты и методы, которые помогут вам избежать ложных срабатываний тестов. Структурируя методы сравнения и хорошо управляя своим временем, вы сможете улучшить надежность и качество тестов.
Первый вариант: Заменить перед сверкой динамическое значение в фактической структуре на ожидаемое.
Второй вариант: Написать свою функцию сравнения полей структуры через reflect с возможностью передачи названий полей, которые не буду сравниваться