diff --git a/src/main/java/play/pluv/music/application/MusicExplorer.java b/src/main/java/play/pluv/music/application/MusicExplorer.java index 5e4ea74..c14547f 100644 --- a/src/main/java/play/pluv/music/application/MusicExplorer.java +++ b/src/main/java/play/pluv/music/application/MusicExplorer.java @@ -1,15 +1,14 @@ package play.pluv.music.application; import java.util.List; -import java.util.Optional; -import play.pluv.music.domain.DestinationMusic; +import play.pluv.music.domain.DestinationMusics; import play.pluv.music.domain.MusicId; import play.pluv.playlist.domain.MusicStreaming; import play.pluv.playlist.domain.PlayListMusic; public interface MusicExplorer { - Optional searchMusic(final String accessToken, final PlayListMusic source); + DestinationMusics searchMusic(final String accessToken, final PlayListMusic source); void transferMusics( final String accessToken, final List musicIds, final String playlistName diff --git a/src/main/java/play/pluv/music/application/MusicExplorerComposite.java b/src/main/java/play/pluv/music/application/MusicExplorerComposite.java index 863738f..ccc89c1 100644 --- a/src/main/java/play/pluv/music/application/MusicExplorerComposite.java +++ b/src/main/java/play/pluv/music/application/MusicExplorerComposite.java @@ -9,7 +9,7 @@ import java.util.Optional; import java.util.Set; import org.springframework.stereotype.Component; -import play.pluv.music.domain.DestinationMusic; +import play.pluv.music.domain.DestinationMusics; import play.pluv.music.domain.MusicId; import play.pluv.music.exception.MusicException; import play.pluv.playlist.domain.MusicStreaming; @@ -25,7 +25,7 @@ public MusicExplorerComposite(final Set musicExplorers) { .collect(toMap(MusicExplorer::supportedType, identity())); } - public Optional searchMusic( + public DestinationMusics searchMusic( final MusicStreaming musicStreaming, final String accessToken, final PlayListMusic source ) { return getClient(musicStreaming).searchMusic(accessToken, source); diff --git a/src/main/java/play/pluv/music/application/MusicService.java b/src/main/java/play/pluv/music/application/MusicService.java index 0be6e7d..009f5ea 100644 --- a/src/main/java/play/pluv/music/application/MusicService.java +++ b/src/main/java/play/pluv/music/application/MusicService.java @@ -1,14 +1,13 @@ package play.pluv.music.application; import java.util.List; -import java.util.Optional; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import play.pluv.music.application.dto.MusicAddRequest; import play.pluv.music.application.dto.MusicSearchRequest; import play.pluv.music.application.dto.MusicSearchRequest.MusicQuery; import play.pluv.music.application.dto.MusicSearchResponse; -import play.pluv.music.domain.DestinationMusic; +import play.pluv.music.domain.DestinationMusics; import play.pluv.music.domain.MusicId; import play.pluv.playlist.domain.MusicStreaming; import play.pluv.playlist.domain.PlayListMusic; @@ -39,14 +38,14 @@ public void transferMusics(final MusicAddRequest request, final MusicStreaming d } private MusicSearchResponse searchMusic( - final MusicStreaming musicStreaming, final PlayListMusic playlistMusic, final String accessToken + final MusicStreaming musicStreaming, final PlayListMusic playlistMusic, + final String accessToken ) { - final Optional result - = musicExplorerComposite.searchMusic(musicStreaming, accessToken, playlistMusic); - return result - .map( - destination -> MusicSearchResponse.createFound(playlistMusic, destination) - ) - .orElseGet(() -> MusicSearchResponse.createNotFound(playlistMusic)); + final DestinationMusics destinationMusics = musicExplorerComposite + .searchMusic(musicStreaming, accessToken, playlistMusic); + if (destinationMusics.isEmpty()) { + return MusicSearchResponse.createNotFound(playlistMusic); + } + return MusicSearchResponse.createFound(playlistMusic, destinationMusics); } } diff --git a/src/main/java/play/pluv/music/application/dto/MusicSearchResponse.java b/src/main/java/play/pluv/music/application/dto/MusicSearchResponse.java index ad91be2..baa2738 100644 --- a/src/main/java/play/pluv/music/application/dto/MusicSearchResponse.java +++ b/src/main/java/play/pluv/music/application/dto/MusicSearchResponse.java @@ -1,8 +1,9 @@ package play.pluv.music.application.dto; +import java.util.List; import lombok.Builder; import play.pluv.music.domain.DestinationMusic; -import play.pluv.music.domain.SourceMusic; +import play.pluv.music.domain.DestinationMusics; import play.pluv.playlist.domain.PlayListMusic; @Builder @@ -10,21 +11,22 @@ public record MusicSearchResponse( Boolean isEqual, Boolean isFound, SourceMusicResponse sourceMusic, - DestinationMusicResponse destinationMusic + List destinationMusics ) { public static MusicSearchResponse createFound( - final PlayListMusic sourceMusic, final DestinationMusic destinationMusic + final PlayListMusic sourceMusic, final DestinationMusics destinationMusics ) { final SourceMusicResponse sourceMusicResponse = SourceMusicResponse.from(sourceMusic); - final DestinationMusicResponse destinationMusicResponse - = DestinationMusicResponse.from(destinationMusic); + final List destinationMusicResponses + = DestinationMusicResponse.extractEqualOrConvert(destinationMusics.getDestinationMusics(), + sourceMusic); return MusicSearchResponse.builder() .sourceMusic(sourceMusicResponse) - .destinationMusic(destinationMusicResponse) + .destinationMusics(destinationMusicResponses) .isFound(true) - .isEqual(destinationMusic.isSame(sourceMusic)) + .isEqual(destinationMusics.containEqual(sourceMusic)) .build(); } @@ -67,5 +69,23 @@ public static DestinationMusicResponse from(final DestinationMusic destinationMu destinationMusic.getImageUrl() ); } + + public static List from( + final List destinationMusics + ) { + return destinationMusics.stream() + .map(DestinationMusicResponse::from) + .toList(); + } + + public static List extractEqualOrConvert( + final List destinationMusics, final PlayListMusic playListMusic + ) { + return destinationMusics.stream() + .filter(destinationMusic -> destinationMusic.isSame(playListMusic)) + .findAny() + .map(destinationMusic -> List.of(DestinationMusicResponse.from(destinationMusic))) + .orElseGet(() -> DestinationMusicResponse.from(destinationMusics)); + } } } diff --git a/src/main/java/play/pluv/music/domain/DestinationMusics.java b/src/main/java/play/pluv/music/domain/DestinationMusics.java new file mode 100644 index 0000000..ec806d4 --- /dev/null +++ b/src/main/java/play/pluv/music/domain/DestinationMusics.java @@ -0,0 +1,24 @@ +package play.pluv.music.domain; + +import java.util.List; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import play.pluv.playlist.domain.PlayListMusic; + +@Getter +@RequiredArgsConstructor +public class DestinationMusics { + + private final List destinationMusics; + + public Boolean containEqual(final PlayListMusic playListMusic) { + return destinationMusics.stream() + .map(destinationMusic -> destinationMusic.isSame(playListMusic)) + //하나라도 동일하면 true 반환 + .reduce(false, (a, b) -> a || b); + } + + public Boolean isEmpty() { + return destinationMusics.isEmpty(); + } +} diff --git a/src/main/java/play/pluv/music/domain/SourceMusic.java b/src/main/java/play/pluv/music/domain/SourceMusic.java deleted file mode 100644 index 9e1e1f9..0000000 --- a/src/main/java/play/pluv/music/domain/SourceMusic.java +++ /dev/null @@ -1,23 +0,0 @@ -package play.pluv.music.domain; - -import java.util.List; -import java.util.Optional; -import lombok.Builder; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -@RequiredArgsConstructor -@Builder -public class SourceMusic { - - @Getter - private final String title; - @Getter - private final List artistNames; - //Nullable - private final String isrcCode; - - public Optional getIsrcCode() { - return Optional.ofNullable(isrcCode); - } -} diff --git a/src/main/java/play/pluv/oauth/google/GoogleConnector.java b/src/main/java/play/pluv/oauth/google/GoogleConnector.java index d9c5288..d9dffc0 100644 --- a/src/main/java/play/pluv/oauth/google/GoogleConnector.java +++ b/src/main/java/play/pluv/oauth/google/GoogleConnector.java @@ -4,14 +4,13 @@ import java.util.ArrayList; import java.util.List; -import java.util.Optional; import java.util.function.Function; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import play.pluv.music.application.MusicExplorer; -import play.pluv.music.domain.DestinationMusic; +import play.pluv.music.domain.DestinationMusics; import play.pluv.music.domain.MusicId; import play.pluv.oauth.application.SocialLoginClient; import play.pluv.oauth.domain.OAuthMemberInfo; @@ -72,12 +71,12 @@ public PlayListId createPlayList(final String accessToken, final String title) { } @Override - public Optional searchMusic( + public DestinationMusics searchMusic( final String accessToken, final PlayListMusic source ) { final String q = source.getTitle() + String.join(",", source.getArtistNames()); return googleApiClient.searchMusic(CREATE_AUTH_HEADER.apply(accessToken), q) - .toDestinationMusic(); + .toDestinationMusics(); } @Override diff --git a/src/main/java/play/pluv/oauth/google/dto/YoutubeSearchMusicResponses.java b/src/main/java/play/pluv/oauth/google/dto/YoutubeSearchMusicResponses.java index ccc848f..ca40a92 100644 --- a/src/main/java/play/pluv/oauth/google/dto/YoutubeSearchMusicResponses.java +++ b/src/main/java/play/pluv/oauth/google/dto/YoutubeSearchMusicResponses.java @@ -5,6 +5,7 @@ import java.util.List; import java.util.Optional; import play.pluv.music.domain.DestinationMusic; +import play.pluv.music.domain.DestinationMusics; import play.pluv.music.domain.MusicId; public record YoutubeSearchMusicResponses( @@ -27,11 +28,28 @@ public Optional toDestinationMusic() { ); } + public DestinationMusics toDestinationMusics() { + return new DestinationMusics(items.stream() + .map(YoutubeMusicVideo::toDestinationMusic) + .limit(5) + .toList()); + } + private record YoutubeMusicVideo( VideoId id, YoutubeMusicDetail snippet ) { + public DestinationMusic toDestinationMusic() { + return DestinationMusic.builder() + .musicId(new MusicId(YOUTUBE, id.videoId())) + .imageUrl(snippet().thumbnails().getUrl()) + .artistNames(List.of()) + .title(snippet().title()) + .isrcCode(null) + .build(); + } + private String getId() { return id.videoId(); } diff --git a/src/main/java/play/pluv/oauth/spotify/SpotifyConnector.java b/src/main/java/play/pluv/oauth/spotify/SpotifyConnector.java index 77a6193..f042a67 100644 --- a/src/main/java/play/pluv/oauth/spotify/SpotifyConnector.java +++ b/src/main/java/play/pluv/oauth/spotify/SpotifyConnector.java @@ -5,14 +5,13 @@ import java.util.ArrayList; import java.util.List; -import java.util.Optional; import java.util.function.Function; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import play.pluv.music.application.MusicExplorer; -import play.pluv.music.domain.DestinationMusic; +import play.pluv.music.domain.DestinationMusics; import play.pluv.music.domain.MusicId; import play.pluv.oauth.application.SocialLoginClient; import play.pluv.oauth.domain.OAuthMemberInfo; @@ -61,12 +60,12 @@ public MusicStreaming supportedType() { } @Override - public Optional searchMusic( + public DestinationMusics searchMusic( final String accessToken, final PlayListMusic source ) { final MultiValueMap param = createRequestParamForSearchMusic(source); return spotifyApiClient.searchMusic(CREATE_AUTH_HEADER.apply(accessToken), param) - .toMusic(); + .toDestinationMusics(); } @Override diff --git a/src/main/java/play/pluv/oauth/spotify/dto/SpotifyMusic.java b/src/main/java/play/pluv/oauth/spotify/dto/SpotifyMusic.java index 8ac14a1..c3c1719 100644 --- a/src/main/java/play/pluv/oauth/spotify/dto/SpotifyMusic.java +++ b/src/main/java/play/pluv/oauth/spotify/dto/SpotifyMusic.java @@ -1,8 +1,12 @@ package play.pluv.oauth.spotify.dto; +import static play.pluv.playlist.domain.MusicStreaming.SPOTIFY; + import com.fasterxml.jackson.databind.PropertyNamingStrategies.SnakeCaseStrategy; import com.fasterxml.jackson.databind.annotation.JsonNaming; import java.util.List; +import play.pluv.music.domain.DestinationMusic; +import play.pluv.music.domain.MusicId; @JsonNaming(SnakeCaseStrategy.class) public record SpotifyMusic( @@ -13,6 +17,16 @@ public record SpotifyMusic( List artists ) { + public DestinationMusic toDestinationMusic() { + return DestinationMusic.builder() + .musicId(new MusicId(SPOTIFY, id())) + .imageUrl(getImageUrl()) + .artistNames(getArtistNames()) + .title(name()) + .isrcCode(getIsrcCode()) + .build(); + } + public String getImageUrl() { //TODO: 추후 로직 수정하기 images가 nullable함 return album.images().get(0).url(); diff --git a/src/main/java/play/pluv/oauth/spotify/dto/SpotifySearchMusicResponse.java b/src/main/java/play/pluv/oauth/spotify/dto/SpotifySearchMusicResponse.java index f1e3314..529e087 100644 --- a/src/main/java/play/pluv/oauth/spotify/dto/SpotifySearchMusicResponse.java +++ b/src/main/java/play/pluv/oauth/spotify/dto/SpotifySearchMusicResponse.java @@ -1,40 +1,25 @@ package play.pluv.oauth.spotify.dto; -import static play.pluv.playlist.domain.MusicStreaming.SPOTIFY; - import java.util.List; -import java.util.Optional; import play.pluv.music.domain.DestinationMusic; -import play.pluv.music.domain.MusicId; +import play.pluv.music.domain.DestinationMusics; public record SpotifySearchMusicResponse( Track tracks ) { - public Optional toMusic() { - return tracks.toMusic(); + public DestinationMusics toDestinationMusics() { + return new DestinationMusics(tracks.toMusics()); } public record Track( List items ) { - public Optional toMusic() { - if (items.isEmpty()) { - return Optional.empty(); - } - - final SpotifyMusic spotifyMusic = items.get(0); - - return Optional.of(DestinationMusic.builder() - .musicId(new MusicId(SPOTIFY, spotifyMusic.id())) - .imageUrl(spotifyMusic.getImageUrl()) - .artistNames(spotifyMusic.getArtistNames()) - .title(spotifyMusic.name()) - .isrcCode(spotifyMusic.getIsrcCode()) - .build() - ); + public List toMusics() { + return items.stream() + .map(SpotifyMusic::toDestinationMusic) + .toList(); } - } } diff --git a/src/test/java/play/pluv/api/MusicApiTest.java b/src/test/java/play/pluv/api/MusicApiTest.java index 8fdf481..622c1ea 100644 --- a/src/test/java/play/pluv/api/MusicApiTest.java +++ b/src/test/java/play/pluv/api/MusicApiTest.java @@ -8,7 +8,6 @@ import static org.springframework.restdocs.payload.JsonFieldType.ARRAY; import static org.springframework.restdocs.payload.JsonFieldType.BOOLEAN; import static org.springframework.restdocs.payload.JsonFieldType.NUMBER; -import static org.springframework.restdocs.payload.JsonFieldType.OBJECT; import static org.springframework.restdocs.payload.JsonFieldType.STRING; import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields; @@ -47,14 +46,14 @@ public class MusicApiTest extends ApiTest { fieldWithPath("data[].sourceMusic.imageUrl").type(STRING).description("음악 imageUrl"), fieldWithPath("data[].sourceMusic.artistName").type(STRING) .description("검색하려는 음악의 가수 이름"), - fieldWithPath("data[].destinationMusic").type(OBJECT).description("조회된 음악 정보") + fieldWithPath("data[].destinationMusics").type(ARRAY).description("조회된 음악 정보") .optional(), - fieldWithPath("data[].destinationMusic.id").type(STRING).description("조회된 음악 id"), - fieldWithPath("data[].destinationMusic.title").type(STRING) + fieldWithPath("data[].destinationMusics[].id").type(STRING).description("조회된 음악 id"), + fieldWithPath("data[].destinationMusics[].title").type(STRING) .description("조회된 음악의 이름"), - fieldWithPath("data[].destinationMusic.artistName").type(STRING) + fieldWithPath("data[].destinationMusics[].artistName").type(STRING) .description("조회된 음악의 가수 이름"), - fieldWithPath("data[].destinationMusic.imageUrl").type(STRING) + fieldWithPath("data[].destinationMusics[].imageUrl").type(STRING) .description("조회된 음악의 커버 이미지 url") )}; private static final Snippet[] TRANSFER_MUSIC_SNIPPET = {requestFields( diff --git a/src/test/java/play/pluv/api/fixture/MusicFixture.java b/src/test/java/play/pluv/api/fixture/MusicFixture.java index a49c4ef..b3f85f8 100644 --- a/src/test/java/play/pluv/api/fixture/MusicFixture.java +++ b/src/test/java/play/pluv/api/fixture/MusicFixture.java @@ -10,6 +10,7 @@ import play.pluv.music.application.dto.MusicSearchResponse.DestinationMusicResponse; import play.pluv.music.application.dto.MusicSearchResponse.SourceMusicResponse; import play.pluv.music.domain.DestinationMusic; +import play.pluv.music.domain.DestinationMusics; import play.pluv.music.domain.MusicId; import play.pluv.playlist.domain.PlayListMusic; @@ -19,14 +20,15 @@ public class MusicFixture { return List.of( new MusicSearchResponse( true, true, new SourceMusicResponse("좋은 날", "아이유", "imageUrl"), - new DestinationMusicResponse("124nkd3fh", "Good Day", "IU", + List.of(new DestinationMusicResponse("124nkd3fh", "Good Day", "IU", "https://i.scdn.co/image/ab67616d00001e0215cf3110f19687b1a24943d1") + ) ), new MusicSearchResponse(true, true, new SourceMusicResponse("ㅈㅣㅂ", "hanro", "imageUrl"), - new DestinationMusicResponse("uo890df1", "SPOT!", "제니,지코", - "https://i.scdn.co/image/ab67616d00001e024930dc9d8cdc7f5f33282538")), + List.of(new DestinationMusicResponse("uo890df1", "SPOT!", "제니,지코", + "https://i.scdn.co/image/ab67616d00001e024930dc9d8cdc7f5f33282538"))), new MusicSearchResponse(false, false, - new SourceMusicResponse("세상에 존재하지 않는 음악", "세상에 존재하지 않는 가수", "imageUrl"), null + new SourceMusicResponse("세상에 존재하지 않는 음악", "세상에 존재하지 않는 가수", "imageUrl"), List.of() ) ); } @@ -35,14 +37,14 @@ true, true, new SourceMusicResponse("좋은 날", "아이유", "imageUrl"), return List.of( new MusicSearchResponse( true, true, new SourceMusicResponse("좋은 날", "아이유", "imageUrl"), - new DestinationMusicResponse("124nkd3fh", "Good Day - MV", "", - "https://i.scdn.co/image/ab67616d00001e0215cf3110f19687b1a24943d1") + List.of(new DestinationMusicResponse("124nkd3fh", "Good Day - MV", "", + "https://i.scdn.co/image/ab67616d00001e0215cf3110f19687b1a24943d1")) ), new MusicSearchResponse(true, true, new SourceMusicResponse("ㅈㅣㅂ", "hanro", "imageUrl"), - new DestinationMusicResponse("uo890df1", "SPOT! - MV (제니,지코)", "", - "https://i.scdn.co/image/ab67616d00001e024930dc9d8cdc7f5f33282538")), + List.of(new DestinationMusicResponse("uo890df1", "SPOT! - MV (제니,지코)", "", + "https://i.scdn.co/image/ab67616d00001e024930dc9d8cdc7f5f33282538"))), new MusicSearchResponse(false, false, - new SourceMusicResponse("세상에 존재하지 않는 음악", "세상에 존재하지 않는 가수", "imageUrl"), null + new SourceMusicResponse("세상에 존재하지 않는 음악", "세상에 존재하지 않는 가수", "imageUrl"), List.of() ) ); } @@ -77,6 +79,17 @@ true, true, new SourceMusicResponse("좋은 날", "아이유", "imageUrl"), ); } + public static DestinationMusics 한로로_집_아이유_좋은날() { + return new DestinationMusics(List.of( + DestinationMusic.builder() + .musicId(new MusicId(SPOTIFY, "musicId1")).title("ㅈㅣㅂ").imageUrl("imageUrl") + .isrcCode(null).artistNames(List.of("한로로")).build(), + DestinationMusic.builder() + .musicId(new MusicId(SPOTIFY, "musicId2")).title("좋은 날").imageUrl("imageUrl") + .isrcCode("KRDDAFA3").artistNames(List.of("아이유")).build() + )); + } + public static List 이전되지_못한_음악_목록() { return List.of( PlayListMusic.builder() diff --git a/src/test/java/play/pluv/music/application/MusicServiceTest.java b/src/test/java/play/pluv/music/application/MusicServiceTest.java index 8cb4a4a..bffe06b 100644 --- a/src/test/java/play/pluv/music/application/MusicServiceTest.java +++ b/src/test/java/play/pluv/music/application/MusicServiceTest.java @@ -27,8 +27,8 @@ class MusicServiceTest extends ApplicationTest { final List actual = musicService.searchMusics(SPOTIFY, request); final List expected = List.of( new MusicSearchResponse(true, true, - new SourceMusicResponse("좋은 날", "아이유","imageUrl"), - new DestinationMusicResponse("goodDayId", "Good Day", "IU", "href") + new SourceMusicResponse("좋은 날", "아이유", "imageUrl"), + List.of(new DestinationMusicResponse("goodDayId", "Good Day", "IU", "href")) ) ); diff --git a/src/test/java/play/pluv/music/domain/DestinationMusicsTest.java b/src/test/java/play/pluv/music/domain/DestinationMusicsTest.java new file mode 100644 index 0000000..d6ef85d --- /dev/null +++ b/src/test/java/play/pluv/music/domain/DestinationMusicsTest.java @@ -0,0 +1,40 @@ +package play.pluv.music.domain; + +import static org.assertj.core.api.Assertions.assertThat; +import static play.pluv.api.fixture.MusicFixture.한로로_집_아이유_좋은날; + +import java.util.List; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import play.pluv.playlist.domain.PlayListMusic; + +class DestinationMusicsTest { + + @Nested + class 원소에_동일한음악이_포함되어있는지 { + + @Test + void 포함되어_있는_경우() { + final DestinationMusics destinationMusics = 한로로_집_아이유_좋은날(); + final PlayListMusic playListMusic + = new PlayListMusic("good day", List.of("iu"), "KRDDAFA3", "image"); + + final Boolean result = destinationMusics.containEqual(playListMusic); + + assertThat(result) + .isTrue(); + } + + @Test + void 포함되어_있지_않은_경우() { + final DestinationMusics destinationMusics = 한로로_집_아이유_좋은날(); + final PlayListMusic playListMusic + = new PlayListMusic("always awake", List.of("재지팩트"), null, "image"); + + final Boolean result = destinationMusics.containEqual(playListMusic); + + assertThat(result) + .isFalse(); + } + } +} \ No newline at end of file diff --git a/src/test/java/play/pluv/support/ApiTest.java b/src/test/java/play/pluv/support/ApiTest.java index efc2485..4b9ea1f 100644 --- a/src/test/java/play/pluv/support/ApiTest.java +++ b/src/test/java/play/pluv/support/ApiTest.java @@ -4,14 +4,12 @@ import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; import com.fasterxml.jackson.databind.ObjectMapper; -import io.swagger.v3.oas.annotations.responses.ApiResponse; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayNameGeneration; import org.junit.jupiter.api.DisplayNameGenerator; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -import org.springframework.boot.test.context.TestConfiguration; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;