Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 23 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<lombok.version>1.18.22</lombok.version>
<junit5.version>5.10.1</junit5.version>
</properties>

<dependencies>
Expand All @@ -25,7 +26,7 @@
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.3.3</version>
<version>42.5.1</version>
<scope>runtime</scope>
</dependency>

Expand All @@ -39,13 +40,32 @@
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.8.2</version>
<version>${junit5.version}</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.assertj/assertj-core -->
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.25.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>${junit5.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>2.1.210</version>
<version>2.2.220</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>4.11.0</version>
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

а не должен совпадать с junit 5 версией?

<scope>test</scope>
</dependency>
</dependencies>
Expand Down
97 changes: 97 additions & 0 deletions src/test/java/com/dmdev/dao/SubscriptionDaoIT.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package com.dmdev.dao;

import com.dmdev.entity.Provider;
import com.dmdev.entity.Status;
import com.dmdev.entity.Subscription;
import com.dmdev.integration.IntegrationTestBase;
import org.junit.jupiter.api.Test;

import java.util.List;
import java.util.Optional;

import static com.dmdev.util.DateUtil.getExpirationDate;
import static org.assertj.core.api.Assertions.assertThat;

class SubscriptionDaoIT extends IntegrationTestBase {

private final SubscriptionDao subDao = SubscriptionDao.getInstance();
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Лучше использовать общепринятые сокращения в именовании всего в Java (sub).


@Test
void whenFindAll_thenAllSubsFound() {
Subscription sub1 = subDao.insert(getSubscription("User 1", 1));
Subscription sub2 = subDao.insert(getSubscription("User 2", 2));
Subscription sub3 = subDao.insert(getSubscription("User 3", 3));

List<Subscription> actualSub = subDao.findAll();
List<Integer> userIds = actualSub.stream().map(Subscription::getUserId).toList();
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

разделяй четко 3 секции в тесте, чтобы улучшить его читабильность: given/when/then


assertThat(userIds).containsExactly(sub1.getUserId(), sub2.getUserId(), sub3.getUserId());
}

@Test
void whenSubExists_thenItIsFindById() {
Subscription expectedSub = subDao.insert(getSubscription("User 1", 1));

Optional<Subscription> actualSub = subDao.findById(expectedSub.getId());

assertThat(actualSub).isPresent();
assertThat(actualSub.get()).isEqualTo(expectedSub);
}

@Test
void whenSubExists_thenItIsDeleted() {
Subscription savedSub = subDao.insert(getSubscription("User 1", 1));
boolean isDeleted = subDao.delete(savedSub.getId());
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is* больше относится к именованию методов, а не локальных переменных
для переменных не нужны эти префиксы


assertThat(isDeleted).isTrue();
}

@Test
void whenSubDoesNotExist_thenItIsNotDeleted() {
subDao.insert(getSubscription("User 1", 1));
boolean isDeleted = subDao.delete(11);

assertThat(isDeleted).isFalse();
}

@Test
void update() {
Subscription insertedSub = subDao.insert(getSubscription("User 1", 1));
insertedSub.setStatus(Status.CANCELED);
insertedSub.setExpirationDate(getExpirationDate("13:50:00, 15.03.2024", "H:m:s, d.M.y"));
subDao.update(insertedSub);

Optional<Subscription> foundSub = subDao.findById(insertedSub.getId());

assertThat(insertedSub).isEqualTo(foundSub.get());

}

@Test
void insert() {
Subscription expectedSub = getSubscription("User 1", 1);
Subscription actualSub = subDao.insert(expectedSub);

assertThat(actualSub.getId()).isNotNull();
}

@Test
void findByUserId() {
Subscription expectedSub = subDao.insert(getSubscription("User 1", 1));

List<Subscription> actualSubs = subDao.findByUserId(expectedSub.getUserId());
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Для теста метода findByUserId нужна вставить несколько подписок с разным userId - иначе некорректный dataset. Его можно заменить на findAll, например, и он все еще будет проходить


assertThat(actualSubs.size()).isEqualTo(1);
assertThat(actualSubs).containsExactly(expectedSub);
}

private Subscription getSubscription(String userName, int userId) {
return Subscription.builder()
.userId(userId)
.name(userName)
.provider(Provider.APPLE)
.expirationDate(getExpirationDate("23:59:59, 31.03.2024", "H:m:s, d.M.y"))
.status(Status.ACTIVE)
.build();
}
}
39 changes: 39 additions & 0 deletions src/test/java/com/dmdev/mapper/CreateSubscriptionMapperTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.dmdev.mapper;

import com.dmdev.dto.CreateSubscriptionDto;
import com.dmdev.entity.Provider;
import com.dmdev.entity.Status;
import com.dmdev.entity.Subscription;
import org.junit.jupiter.api.Test;

import static com.dmdev.util.DateUtil.getExpirationDate;
import static org.assertj.core.api.Assertions.assertThat;

class CreateSubscriptionMapperTest {

private final CreateSubscriptionMapper mapper = CreateSubscriptionMapper.getInstance();

@Test
void whenValidDto_thenValidSubscriptionCreated() {
String date = "21:00:00, 30.04.2024";
String datePattern = "H:m:s, d.M.y";
CreateSubscriptionDto validDto = CreateSubscriptionDto.builder()
.userId(1)
.name("Test Name")
.provider(Provider.GOOGLE.name())
.expirationDate(getExpirationDate(date, datePattern))
.build();

Subscription actualSub = mapper.map(validDto);
Subscription expectedSub = Subscription.builder()
.userId(1)
.name("Test Name")
.provider(Provider.GOOGLE)
.expirationDate(getExpirationDate(date, datePattern))
.status(Status.ACTIVE)
.build();

assertThat(actualSub).isEqualTo(expectedSub);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

не надо оставлять пустые строки в конце метода

}
}
149 changes: 149 additions & 0 deletions src/test/java/com/dmdev/service/SubscriptionServiceTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
package com.dmdev.service;

import com.dmdev.dao.SubscriptionDao;
import com.dmdev.dto.CreateSubscriptionDto;
import com.dmdev.entity.Provider;
import com.dmdev.entity.Status;
import com.dmdev.entity.Subscription;
import com.dmdev.exception.SubscriptionException;
import com.dmdev.exception.ValidationException;
import com.dmdev.mapper.CreateSubscriptionMapper;
import com.dmdev.validator.CreateSubscriptionValidator;
import com.dmdev.validator.Error;
import com.dmdev.validator.ValidationResult;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.AdditionalAnswers;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.time.Clock;
import java.time.Instant;
import java.time.ZoneId;
import java.util.Collections;
import java.util.Optional;

import static com.dmdev.util.DateUtil.getExpirationDate;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Подправь в настройках idea, чтобы не схлопывало импорты в *


@ExtendWith(MockitoExtension.class)
class SubscriptionServiceTest {

@Mock
private SubscriptionDao subDao;
@Mock
private CreateSubscriptionMapper createSubMapper;
@Mock
private CreateSubscriptionValidator createSubValidator;
@Mock
private Clock clock = Clock.fixed(Instant.now(), ZoneId.systemDefault());
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

это не мок


@InjectMocks
private SubscriptionService subService; // );
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

не оставляй закоментированного кода


@Test
void whenSubscriptionExists_thenUpdateAndInsert() {
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

не может сразу и обновить и вставить метод
Плохое название тестового метода - не говорит что именно тестируется в нем

CreateSubscriptionDto dto = CreateSubscriptionDto.builder()
.userId(23)
.name("Test Name")
.provider(Provider.APPLE.name())
.expirationDate(getExpirationDate("21:00:00, 30.04.2024", "H:m:s, d.M.y"))
.build();

Subscription existingSub = Subscription.builder()
.userId(23)
.name("Test Name")
.provider(Provider.APPLE)
.expirationDate(getExpirationDate("23:59:59, 31.03.2024", "H:m:s, d.M.y"))
.build();

Subscription updatedSub = Subscription.builder()
.userId(23)
.name("Test Name")
.provider(Provider.APPLE)
.expirationDate(dto.getExpirationDate())
.status(Status.ACTIVE)
.build();

doReturn(new ValidationResult()).when(createSubValidator).validate(dto);
doReturn(Collections.singletonList(existingSub)).when(subDao).findByUserId(dto.getUserId());

// Ask stub DAO.upsert() method to return whichever argument it receives
when(subDao.upsert(any(Subscription.class))).then(AdditionalAnswers.returnsFirstArg());

assertThat(subService.upsert(dto)).isEqualTo(updatedSub);
verify(subDao).upsert(existingSub);

}

@Test
void whenInvalidDto_thenExceptionThrown() {
CreateSubscriptionDto invalidDto = CreateSubscriptionDto.builder().build();
ValidationResult invalidResult = new ValidationResult();
invalidResult.add(Error.of(100, "userId is invalid"));
doReturn(invalidResult).when(createSubValidator).validate(invalidDto);

assertThrows(ValidationException.class, () -> subService.upsert(invalidDto));
verifyNoInteractions(subDao, createSubMapper);
}

@Test
void whenActiveSubscriptionExists_thenCancelIt() {
Subscription existingSub = Subscription.builder()
.id(243)
.userId(23)
.name("Test Name")
.provider(Provider.APPLE)
.expirationDate(getExpirationDate("23:59:59, 31.03.2024", "H:m:s, d.M.y"))
.status(Status.ACTIVE)
.build();

doReturn(Optional.of(existingSub)).when(subDao).findById(any());
// Ask stub DAO.update() method to return whichever argument it receives
when(subDao.update(any(Subscription.class))).then(AdditionalAnswers.returnsFirstArg());

subService.cancel(existingSub.getId());

assertThat(existingSub.getStatus()).isEqualTo(Status.CANCELED);
verify(subDao).update(existingSub);
}

@Test
void whenSubIdNotExists_thenExceptionThrown() {
doReturn(Optional.empty()).when(subDao).findById(anyInt());
assertThrows(IllegalArgumentException.class, () -> subService.cancel(anyInt()));
}

@Test
void whenSubIsActive_thenExceptionThrown() {
doReturn(Optional.of(Subscription.builder().status(Status.EXPIRED).build()))
.when(subDao).findById(anyInt());
assertThrows(SubscriptionException.class, () -> subService.cancel(anyInt()));
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Нужно проверить какое именно исключение упало, например, код/текст исключения и т.д. Иначе причина может поменяться, а тест будет проходить

}

@Test
void whenNonExpiredSubscriptionExists_thenExpireIt() {
Subscription existingSub = Subscription.builder()
.id(243)
.userId(23)
.name("Test Name")
.provider(Provider.APPLE)
.expirationDate(getExpirationDate("23:59:59, 31.03.2024", "H:m:s, d.M.y"))
.status(Status.ACTIVE)
.build();

doReturn(Optional.of(existingSub)).when(subDao).findById(any());
// Ask stub DAO.update() method to return whichever argument it receives
when(subDao.update(any(Subscription.class))).then(AdditionalAnswers.returnsFirstArg());

subService.expire(existingSub.getId());

assertThat(existingSub.getStatus()).isEqualTo(Status.EXPIRED);
assertThat(existingSub.getExpirationDate()).isEqualTo(Instant.now(clock));
verify(subDao).update(existingSub);
}
}
18 changes: 18 additions & 0 deletions src/test/java/com/dmdev/util/DateUtil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.dmdev.util;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Locale;

public class DateUtil {
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

если это утилитный класс - то final класс и private консторуктор

public static Instant getExpirationDate(String stringDate, String pattern) {
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(pattern, Locale.getDefault());
LocalDateTime localDateTime = LocalDateTime.parse(stringDate, dateTimeFormatter);
ZoneId zoneId = ZoneId.systemDefault();
ZonedDateTime zonedDateTime = localDateTime.atZone(zoneId);
return zonedDateTime.toInstant();
}
}
29 changes: 29 additions & 0 deletions src/test/java/com/dmdev/util/PropertiesUtilTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.dmdev.util;

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import java.util.stream.Stream;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.params.provider.Arguments.arguments;

class PropertiesUtilTest {

public static Stream<Arguments> propertiesProvider() {
return Stream.of(
arguments("db.url", "jdbc:h2:mem:test;DB_CLOSE_DELAY=-1"),
arguments("db.user", "sa"),
arguments("db.driver", "org.h2.Driver")
);
}

@ParameterizedTest
@MethodSource("propertiesProvider")
void whenPropertiesProvided_thenPropertiesSet(String key, String expectedValue) {
String actualValue = PropertiesUtil.get(key);
assertThat(actualValue).isEqualTo(expectedValue);
}

}
Loading