참고강의 - 백기선님의 더 자바, 애플리케이션을 테스트하는 다양한 방법
JUnit은 테스트 순서가 정해져있는게 아니기 때문에 테스트 메서드마다 클래스 인스턴스를 새로 만들어서 실행함(해시 값을 찍어 확인해 보면 서로 다른 인스턴스) -> 의존성을 줄이기 위해
변경 방법이 JUnit5에 생김
클래스 당 인스턴스를 하나만 만들어서 공유하는 방법이 생김
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class StudyTest {
// 클래스당 테스트 인스턴스를 만들면 static일 필요 없음
@BeforeAll
void beforeall() {System.out.println("before all");}
int value = 1;
@FastTest
@DisplayName("스터디 만들기 fast")
void create_new_study() {
System.out.println(value++);
Study actual = new Study(value++);
}
}
테스트 순서는 내부적으로 정해진 순서가 있긴함. 하지만 내부 구성 로직에 따라 순서는 바뀔 수 있으므로 이 순서에 의존하면 안됨.
하지만 경우에 따라서 시나리오 테스트 등.. 인스턴스를 하나만 만들어서 공유해서 사용하기도 해야함
특정 순서대로 테스트를 실행하고 싶을 때, 테스트 메소드를 원하는 순서에 따라 실행하도록 @TestInstance(Lifecycle.PER_CLASS)와 함께 @TestMethodOrder를 사용할 수 있다.
@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class StudyTest {
int value = 1;
@Order(2)
@FastTest
@DisplayName("스터디 만들기 fast")
void create_new_study() {
System.out.println(value++);
Study actual = new Study(value++); // 항상 1임 -> 테스트 매서드마다 클래스 인스턴스를 새로 만들기 때문 -> 테스트 마다 의존성을 줄이기 위해
}
@Order(1)
@Test
@DisplayName("스터디 만들기 slow")
@Tag("slow")
void create_new_study_again() {
System.out.println("create1");
}
}
slow가 fast보다 먼저 실행됨
JUnit 설정 파일로, 클래스패스 루트 (src/test/resources/)에 넣어두면 적용된다.
테스트 인스턴스 라이프사이클 설정
junit.jupiter.testinstance.lifecycle.default = per_class
확장팩 자동 감지 기능
junit.jupiter.extensions.autodetection.enabled = true
@Disabled 무시하고 실행하기
junit.jupiter.conditions.deactivate = org.junit.*DisabledCondition
테스트 이름 표기 전략 설정
junit.jupiter.displayname.generator.default = \
org.junit.jupiter.api.DisplayNameGenerator$ReplaceUnderscores
참고강의 - 백기선님의 더 자바, 애플리케이션을 테스트하는 다양한 방법
스프링부트 2.2+ 프로젝트 생성시 spring-boot-starter-test에서 자동으로 Mockito 추가해줌
Mockito는 특정한 클래스나 인터페이스의 가짜 객체를 만들어 주는 것
다음 세 가지만 알면 Mock을 활용한 테스트를 쉽게 작성할 수 있다
(1) Mock을 만드는 방법
(2) Mock이 어떻게 동작해야 하는지 관리하는 방법
(3) Mock의 행동을 검증하는 방법
MemberService memberService = mock(MemberService.class);
StudyRepository studyRepository = mock(StudyRepository.class);
@ExtendWith(MockitoExtension.class)
class StudyServiceTest {
@Mock MemberService memberService;
@Mock StudyRepository studyRepository;
예제 코드
@ExtendWith(MockitoExtension.class)
class StudyServiceTest {
@Test
void createStudyService(@Mock MemberService memberservice, @Mock StudyRepository studyRepository) {
Optional<Member> optional = memberService.findById(1L);
StudyService studyService = new StudyService(memberService, studyRepository);
assertNotNull(studyService);
}
}
@ExtendWith(MockitoExtension.class)
class StudyServiceTest {
@Test
void createStudyService(@Mock MemberService memberservice, @Mock StudyRepository studyRepository) {
StudyService studyService = new StudyService(memberService, studyRepository);
assertNotNull(studyService);
Member member = new Member();
member.setId(1L);
member.setEmail("dbfgml980413@gmail.com");
when(memberService.findById(1L)).thenReturn(Optional.of(member));
Study study = new Study(10, "java");
Optional<Member> findById = memberService.findById(1L);
assertEquals("dbfgml980413@gmail.com", findById.get().getEmail());
//studyService.createNewStudy(1L, study);
}
}
Mock 객체를 조작해서
BDD
: 애플리케이션이 어떻게 “행동”해야 하는지에 대한 공통된 이해를 구성하는 방법으로, TDD에서 창안했다.
행동에 대한 스펙
- Title
- Narrative
- As a/ I want /so that
Mockito는 BddMockito라는 클래스를 통해 BDD 스타일의 API를 제공한다.
When -> Given
given(memberService.findById(1L)).willReturn(Optional.of(member));
given(studyRepository.save(study)).willReturn(study);
Verify -> Then
then(memberService).should(times(1)).notify(study);
then(memberService).shouldHaveNoMoreInteractions();
알고리즘 기본 문제부터 다지기 시작~~!
람다 함수란? 익명의 함수다.
기본 함수와 람다를 활용한 코드의 예시로 비교해보자
def plus_one(x):
return x + 1
print(plus_one(1))
plus_two = lambda x: x+2
print(plus_two(1))
람다 함수는 내장 함수의 인자로 활용될 때 굉장히 효율적임
-> 보통 sort()에서 사용
def plus_one(x):
return x + 1
a[1, 2, 3]
print(list(map(plus_one, a)))
# 람다 활용
print(list(map(lambda x: x+1, a)))
python 반올림 함수 round()는 round_half_even 방식을 택한다.
-> round_half_even = 짝수쪽으로 근사값을 해버린다.
-> round_half_up = 0.5를 넘어가면 큰수로 반올림 해버린다.
a = 4.500
print(round(a)) // 4 출력
a = 4.5111
print(round(a)) // 5 출력
a = 5.5000
print(round(a)) // 6 출력
간단하게 0.5를 더한 후 int로 바꾸면 된다
a = 67.5
a = a + 0.5
a = int(a)
print(a)
간단한 문제들을 풀어보았다.
from sys import stdin
n, k = map(int, stdin.readline().split())
cnt = 0
for i in range(1, n+1):
if n % i == 0:
cnt += 1
if cnt == k:
print(i)
break
else:
print(-1)
# K번째 수
from sys import stdin
T = int(stdin.readline())
for t in range(T):
n, s, e, k = map(int, stdin.readline().split())
nums = list(map(int, stdin.readline().split()))
nums = nums[s-1:e]
nums.sort()
print("#%d %d" %(t+1, nums[k-1]))
# K번째 큰 수
from sys import stdin
n, k = map(int, stdin.readline().split())
cards = list(map(int, stdin.readline().split()))
res = set()
for i in range(n):
for j in range(i+1, n):
for m in range(j+1, n):
res.add(cards[i] + cards[j] + cards[m])
res = list(res)
res.sort(reverse=True)
print(res[k-1])
from sys import stdin
n = int(stdin.readline())
a = list(map(int, stdin.readline().split()))
ave = round(sum(a)/n)
min = 2147000000
for idx, x in enumerate(a):
tmp = abs(x - ave)
if tmp < min:
min = tmp
score = x
res = idx + 1
elif tmp == min:
if x > score:
score = x
res = idx + 1
print(ave, res)
from sys import stdin
n, m = map(int, stdin.readline())
cnt = [0] * (n+m+3)
max = -2147000000
for i in range(1, n+1):
for j in range(1, m+1):
cnt[i+j] += 1
for i in range(n+m+1):
if cnt[i] > max:
max = cnt[i]
for i in range(n+m+1):
if cnt[i] == max:
print(i, end=' ')
from sys import stdin
n = int(stdin.readline())
a = list(map(int, stdin.readline().split()))
# def digit_sum(x):
# sum = 0
# while x > 0:
# sum += x%10
# x = x//10
def digit_sum(x):
sum = 0
for i in str(x):
sum += int(i)
return sum
max = -2147000000
for x in a:
total = digit_sum(x)
if total > max:
max = total
res = x
print(res)
체 = 걸러낸다는표현으로 2부터 n까지 for문을 돌면서 해당 i번째 수의 배수들을 방문표시함으로써 배수들을 걸러냄 (= 소수만 구할 수 있음)
from sys import stdin
n = int(stdin.readline())
ch = [0] * (n+1)
cnt = 0
for i in range(2, n+1):
if ch[i] == 0:
cnt += 1
for j in range(i, n+1, i):
ch[j] = 1
print(cnt)
자연수를 뒤집은 후 그 수가 소수인지 판별하는 문제
# 뒤집은 소수
from sys import stdin
def reverse(x):
res = 0
while x > 0:
t = x % 10
res = res * 10 + t
x = x // 10
return res
def isPrime(x):
if x == 1:
return False
for i in range(2, x//2 + 1):
if x % i == 0:
return False
else:
return True
n = int(stdin.readline())
a = list(map(int, stdin.readline().split()))
for x in a:
tmp = reverse(x)
if isPrime(tmp):
print(tmp, end=' ')
[문제 링크] https://www.acmicpc.net/problem/2506
from sys import stdin
n = int(stdin.readline())
a = list(map(int, stdin.readline().split()))
sum = 0
cnt = 0
for x in a:
if x == 1:
cnt += 1
sum += cnt
else:
cnt = 0
print(sum)
알고리즘 문제를 풀며 딕셔너리 서브 클래스 2개와 extend()메서드에 대해 정리해보았습니다.
defaultdict()는 딕셔너리를 만드는 dict클래스의 서브 클래스입니다.
기본 딕셔너리에서 존재하지 않는 키를 조회할 경우 KeyError가 발생합니다.
하지만 defaultdict는 존재하지 않는 키를 조회하면 에러가 나는 것이 아니라 해당 키에 대해 인자로 주어진 기본값을 딕셔너리값의 초기값으로 지정할 수 있습니다.
int, list, set 등으로 초기화 할 수 있기 때문에 이를 잘 활용하면 매우 수월하게 코드를 짤 수 있습니다.
from collections import defaultdict
node = defaultdict(list)
node[1] = ['우리집', '강아지']
node[2] = ['두부']
print(node)
print(node[3])
print(node)
// 결과
defaultdict(<class 'list'>, {1: ['우리집', '강아지'], 2: ['두부']})
[]
defaultdict(<class 'list'>, {1: ['우리집', '강아지'], 2: ['두부'], 3: []})
보면 3이라는 key가 node 딕셔너리에 없지만, 에러를 내보내지 않고 빈 리스트로 초기화 된 것을 확인하 ㄹ수 있습니다! 참고로 int로 초기화를 하면 node[3]은 0으로 초기화가 됩니다.
python collections모듈의 Counter 클래스를 사용하면 리스트 해당 원소가 리스트 내에서 몇개로 구성되어 있는지 확인할 수 있습니다. 문자열도 가능합니다.
예시 코드로 확인해보겠습니다.
from collections import Counter
counter = Counter('HelloWorld')
print(counter)
// 결과
Counter({'l': 3, 'o': 2, 'H': 1, 'e': 1, 'W': 1, 'r': 1, 'd': 1})
리스트 예시
from collections import Counter
test_list = ['우리집', '강아지', '두부', '두부', '개껌을', '좋아해', '개껌을', '두부']
counter = Counter(test_list)
print(counter.most_common(2))
// 결과
[('두부', 3), ('개껌을', 2)]
갯수가 많은 순서로 출력을 해줄 수도 있지만 most_common 메서드를 활용하면 갯수가 많은 순으로 원하는 갯수많은 list로 리턴받을 수 있습니다. 위 예시는 가장 원소 갯수가 많았던 두개를 뽑아낸 코드입니다.
다양한 연산도 실행할 수 있습니다.
subtract 예시
from collections import Counter
c = Counter(a=4, b=2, c=0, d=-1)
d = Counter(a=1, b=2, c=3, d=4)
c.subtract(d)
print(c)
// 결과
Counter({'a': 3, 'b': 0, 'c': -3, 'd': -5})
[참고 블로그] https://m.blog.naver.com/wideeyed/221541104629
파이썬 리스트에 원소를 추가하는 방법은 append(x)와 extend(iterable)가 있고 두 함수의 차이점을 알아보겠습니다.
list.append(x)는 리스트 마지막에 x 원소 1개를 그대로 넣는 것이고
list.extend(iterable)는 리스트 끝에 가장 바깥쪽 iterable의 모든 항목을 넣습니다.
a = ['우리집', '두부']
b = ['개껌을', '좋아해']
a.append(b)
print(a)
//결과
['우리집', '두부', ['개껌을', '좋아해']]
a = ['우리집', '두부']
b = ['개껌을', '좋아해']
a.extend(b)
print(a)
//결과
['우리집', '두부', '개껌을', '좋아해']
a = ['우리집', '두부']
b = '돼지야'
a.extend(b)
print(a)
//결과
['우리집', '두부', '돼', '지', '야']
a = ['우리집', '두부']
b = [['그래서', '돼지야']]
a.extend(b)
print(a)
//결과
['우리집', '두부', ['그래서', '돼지야']]
탐색 & 시뮬레이션과 관련한 문제 풀이를 진행했다
# 회문 문자열 검사
from sys import stdin
n = int(stdin.readline())
for i in range(n):
s = stdin.readline()
s = s.upper() # 문자열 대문자화
size = len(s)
# for j in range(size//2):
# if s[j] != s[-1-j]:
# print("#%d NO" %(i+1))
# break
# else:
# print("#%d YES" %(i+1))
if s == s[::-1]:
print("#%d YES" %(i+1))
else:
print("#%d No" %(i+1))
- isdigit() : 정말 숫자를 다 찾음 (ex 2의 제곱)
- isdecimal() : 0~9까지의 숫자만 찾아줌
# 숫자만 추출
from sys import stdin
s = stdin.readline()
res = 0
for x in s:
if x.isdecimal():
res = res * 10 + int(x)
print(res)
cnt = 0
for i in range(1, res+1):
if res % i == 0:
cnt += 1
print(cnt)
# 카드 역배치
from sys import stdin
a = list(range(21))
for _ in range(10):
s, e = map(int, stdin.readline())
for i in range((e-s+1)//2):
a[s+i], a[e-i] = a[e-i], a[s+i] # 스와프
a.pop(0)
for x in a:
print(x, end=' ')
[시간복잡도 비교]
sort() = NlogN 퀵정렬
정렬이 이미 되어져 있는 것을 활용하면: N
-> 데이터가 커질 수록 굉장히 큰 차이임
# 두 리스트 합치기
from sys import stdin
n = int(stdin.readline())
a = list(map(int, stdin.readline()))
m = int(stdin.readline())
b = list(map(int, stdin.readline()))
p1 = p2 = 0
c = []
while p1 < n and p2 < m:
if a[p1] <= b[p2]:
c.append(a[p1])
p1 += 1
else:
c.append(b[p2])
p2 += 1
if p1 < n:
c = c + a[p1:]
if p2 < m:
c = c + b[p2:]
for x in c:
print(x, end=' ')
# 수들의 합
from sys import stdin
n, m = map(int, stdin.readline().split())
a = list(map(int, stdin.readline()))
lt = 0
rt = 1
total = a[0]
cnt = 0
while True:
if total < m:
if rt < n:
total += a[rt]
rt += 1
else:
break
elif total == m:
cnt += 1
total -= a[lt]
lt += 1
else:
total -= a[lt]
lt += 1
print(cnt)
# 격자판 최대합
from sys import stdin
n = int(stdin.readline())
a = [list(map(int, stdin.readline())) for _ in range(n)]
largest = -2147000000
for i in range(n):
sum1 = sum2 = 0
for j in range(n):
sum1 += a[i][j]
sum2 += a[j][i]
if sum1 > largest:
largest = sum1
if sum2 > largest:
largest = sum2
sum1 = sum2 = 0
for i in range(n):
sum1 += a[i][i]
sum2 += a[i][n-i-1]
if sum1 > largest:
largest = sum1
if sum2 > largest:
largest = sum2
print(largest)