Skip to content

[토비의 스프링] 6.5.2_DefaultAdvisorAutoProxyCreator 챕터에서의 테스트 실패 #113

@taxol1203

Description

@taxol1203

클래스 필터를 사용하여 프록시 자동생성을 시도해 보았습니다.

481 ~ 486p에 따라 코드를 수정하고 테스트를 진행하였는데, UserServiceTest가 실패하였습니다.

코드 상 실수나 접근이 잘 못되었는지 확인 부탁드립니다.

실패한 테스트는 upgradeLevelsupgradeAllOrNothing입니다.

upgradeLevels 실패

우선 483p에서 userServiceImpl의 빈의 아이디를 userService로 바꾸어 주었습니다.

<!-- 이제 다시 userService 아이디를 사용할  있다. -->
<bean id = "userService" class="com.taxol.service.UserServiceImpl">
    <property name="userDao" ref="userDao"/>
    <property name="mailSender" ref="mailSender"/>
</bean>

이에따라 기존의 UserServiceImpl는 DI를 할 수 없어 제거하였습니다.

@Autowired
UserServiceImpl userServiceImpl; // 제거하였음

이는 UserServiceTestupgradeLevels 메서드에서 오류가 나는데,
기존의 코드는

@Test
	@DirtiesContext
	public void upgradeLevels() {
		userDao.deleteAll();
		for(User user : users) userDao.add(user);
		
		// 메일 발송 결과를 테스트할 수 있도록 목 오브젝트를 만들어 userService의 의존 오브젝트로 주입한다.
        MockMailSender mockMailSender = new MockMailSender();
        userServiceImpl.setMailSender(mockMailSender);
        
		userService.upgradeLevels();
		
		// 각 사용자별로 업그레이드 후의 예상 레벨을 검증한다.
		checkLevelUpgraded(users.get(0), false);
		checkLevelUpgraded(users.get(1), true);
		checkLevelUpgraded(users.get(2), false);
		checkLevelUpgraded(users.get(3), true);
		checkLevelUpgraded(users.get(4), false);
		
		// 목 오브젝트에 저장된 메일 수신자 목록을 가져와 업그레이드 대상과 일치하는지 확인 한다.
        List<String> request = mockMailSender.getRequests();
        assertThat(request.size(), is(2));
        assertThat(request.get(0), is(users.get(1).getEmail()));
        assertThat(request.get(1), is(users.get(3).getEmail()));
	}

와 같습니다.

여기서 userServiceImpl.setMailSender(mockMailSender);가 오류가 나, 이 코드를 제거하였더니 결과가

java.long.AssertionError:
   Expectd: is <2>
     but wsas <0>

으로 제대로 동작하질 않습니다.

upgradeAllOrNothing 실패

upgradeAllOrNothing의 코드의

fail("TestUserService Exception expected");

을 통해 실패하였습니다.

아예 this.testUserService.upgradeLevels(); 코드가 실패하였다고 생각합니다.

전체 코드

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
	
	<!-- <bean id="connectionMaker" class="com.taxol.chapter1_8.DConnectionMaker" /> -->
	<bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
		<property name="driverClass" value="com.mysql.jdbc.Driver"/>
		<property name="url" value="jdbc:mysql://localhost:3306/tobyspring?useSSL=false"/>
		<property name="username" value="ssafy"/>
		<property name="password" value="ssafy"/>
	</bean>
	
	<bean id="userDao" class="com.taxol.dao.UserDaoJdbc">
		<property name="dataSource" ref="dataSource"/>
	</bean>
	<!-- 이제 userService는 userServiceTx의 오브젝트가 주입 된다. -->
	<!-- <bean id="userService" class ="com.taxol.service.UserServiceTx">
		<property name="transactionManager" ref="transactionManager"></property>
		<property name="userService" ref="userServiceImpl"></property>
	</bean> -->
	
	<!-- UserService에 대한 트랜잭션 프록시 팩토리 빈 -->
	<bean id="userService" class="org.springframework.aop.framework.ProxyFactoryBean">
		<property name="target" ref="userServiceImpl" />
		<property name="interceptorNames">
			<list>
				<value>transactionAdvisor</value>
			</list>
		</property>
	</bean>
		
	<bean id = "userServiceImpl" class="com.taxol.service.UserServiceImpl">
		<property name="userDao" ref="userDao"/>
		<property name="mailSender" ref="mailSender"/>
	</bean>
	<bean id = "transactionManager"
		  class = "org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref = "dataSource"></property>	  
	</bean>
	<bean id="mailSender" class = "com.taxol.service.DummyMailSender"/>
	
	<!-- 어드바이스 -->
	<bean id ="transactionAdvice" class="com.taxol.service.TransactionAdvice">
		<property name="transactionManager" ref="transactionManager"/>
	</bean>
	
	<!-- 스프링이 제공하는 포인트컷 클래스 사용 -->
	<bean id ="transactionPointcut" class="org.springframework.aop.support.NameMatchMethodPointcut">
		<property name="mappedName" value="upgrade*"/>
	</bean>
	
	<!-- 어드바이저 -->
	<bean id ="transactionAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
		<property name="advice" ref="transactionAdvice"/>
		<property name="pointcut" ref="transactionPointcut"/>
	</bean>
</beans>

UserServiceTest

package com.taxol.proxy;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.fail;

import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.List;

import javax.sql.DataSource;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.MailSender;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.PlatformTransactionManager;

import com.taxol.dao.UserDao;
import com.taxol.domain.Level;
import com.taxol.domain.User;
import com.taxol.service.DummyMailSender;
import com.taxol.service.MockMailSender;
import com.taxol.service.TransactionHandler;
import com.taxol.service.UserService;
import com.taxol.service.UserServiceImpl;
import com.taxol.service.UserServiceTx;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "/applicationcontext.xml")
public class UserServiceTest {
	private static final int MIN_LOGCOUNT_FOR_SIVER = 50;
	private static final int MIN_RECOMMEND_FOR_GOLD = 30;

	@Autowired
	UserService userService;
	@Autowired
	UserService testUserService;
	
	@Autowired
	UserDao userDao;
	
	List<User> users; // 테스트 픽스처
	
	@Autowired
	private DataSource dataSource;
	@Autowired
	private PlatformTransactionManager transactionManager;
	@Autowired
	MailSender mailSender;
	@Before
	public void setUp() throws Exception {
		users = Arrays.asList( // 배열을 리스트로 만들어주는 편리한 메소드, 배열을 가변인자로 넣어주면 더욱 편리하다.
			new User("bumjin",   "박범진", "p1", Level.BASIC,  MIN_LOGCOUNT_FOR_SIVER-1, 0, "taxol1203@naver.com"),
			new User("joytouch", "강명성", "p2", Level.BASIC,  MIN_LOGCOUNT_FOR_SIVER, 0, "taxol1203@gmail.com"),
			new User("erwins",   "신승한", "p3", Level.SILVER, 60, MIN_RECOMMEND_FOR_GOLD-1, "taxol1203@daum.com"),
			new User("madnitel", "이상호", "p4", Level.SILVER, 60, MIN_RECOMMEND_FOR_GOLD, "taxol1203@kakao.com"),
			new User("green",    "오민규", "p5", Level.GOLD,  100, Integer.MAX_VALUE, "taxol1203@hanmail.net")
		);
	}

	// 5-17 userService 빈의 주입을 확인하는 테스트
	@Test
	public void bean() {
		assertNotNull(this.userService);
	}

	// 5-30 개선한 레벨 업그레이드 테스트
	@Test
	@DirtiesContext
	public void upgradeLevels() {
		userDao.deleteAll();
		for(User user : users) userDao.add(user);
		
		// 메일 발송 결과를 테스트할 수 있도록 목 오브젝트를 만들어 userService의 의존 오브젝트로 주입한다.
        MockMailSender mockMailSender = new MockMailSender();
        //userServiceImpl.setMailSender(mockMailSender);
        
		userService.upgradeLevels();
		
		// 각 사용자별로 업그레이드 후의 예상 레벨을 검증한다.
		checkLevelUpgraded(users.get(0), false);
		checkLevelUpgraded(users.get(1), true);
		checkLevelUpgraded(users.get(2), false);
		checkLevelUpgraded(users.get(3), true);
		checkLevelUpgraded(users.get(4), false);
		
		// 목 오브젝트에 저장된 메일 수신자 목록을 가져와 업그레이드 대상과 일치하는지 확인 한다.
        List<String> request = mockMailSender.getRequests();
        assertThat(request.size(), is(2));
        assertThat(request.get(0), is(users.get(1).getEmail()));
        assertThat(request.get(1), is(users.get(3).getEmail()));
	}
	
	// DB에서 사용자 정보를 가져와 레벨을 확인하는 코드가 중복되므로 헬퍼 메소드로 분리했다.
	private void checkLevelUpgraded(User user, boolean upgraded) {
		User userUpdate = userDao.get(user.getId());
		if(upgraded) {
			assertEquals(userUpdate.getLevel(), user.getLevel().nextLevel()); // 업그레이드가 일어났는지 확인
		}
		else { 
			assertEquals(userUpdate.getLevel(), user.getLevel()); // 업그레이드가 일어나지 않았는지 확인
		}
	}
	
	// 5-21 add() 메소드의 테스트
	@Test
	public void add() {
		userDao.deleteAll();
		
		User userWithLevel = users.get(4); // GOLD 레벨
		User userWithoutLevel = users.get(0);
		userWithoutLevel.setLevel(null); // 레벨이 비어 있는 사용자. 로직에 따라 등록 중에 BASIC 레벨도 설정돼야 한다.
		
		userService.add(userWithLevel);
		userService.add(userWithoutLevel);
		
		// DB에 저장된 결과를 가져와 확인한다.
		User userWithLevelRead = userDao.get(userWithLevel.getId());
		User userWithoutLevelRead = userDao.get(userWithoutLevel.getId());
		
		assertEquals(userWithLevelRead.getLevel(), userWithLevel.getLevel());
		assertEquals(userWithoutLevelRead.getLevel(), Level.BASIC);
	}
	
	@Test
	public void upgradeAllOrNothing() throws Exception {
		userDao.deleteAll();
		for (User user : users) {
			userDao.add(user);
		}

		try {
			this.testUserService.upgradeLevels();
			fail("TestUserService Exception expected");
		} catch (TestUserServiceException e) {
			System.out.println("test failed");
		}

		checkLevelUpgraded(users.get(0), false);
		checkLevelUpgraded(users.get(1), false);
		checkLevelUpgraded(users.get(2), false);
		checkLevelUpgraded(users.get(3), false);
		checkLevelUpgraded(users.get(4), false);
	}
	
	
	static class TestUserServiceImpl  extends UserServiceImpl {
		private String id = "madnite1";  // 테스트 픽스처의 값을 이제 가져올 수 없기에 고정

		protected void upgradeLevel(User user) { 
			if (user.getId().equals(this.id)) {
				throw new TestUserServiceException();
			}
			super.upgradeLevel(user);
		}
	}
	static class TestUserServiceException extends RuntimeException {
	}
}

NameMatchClassMethodPointcut

package com.taxol.proxy;

import org.springframework.aop.ClassFilter;
import org.springframework.aop.support.NameMatchMethodPointcut;
import org.springframework.util.PatternMatchUtils;

public class NameMatchClassMethodPointcut extends NameMatchMethodPointcut {
    // 모든 클래스를 다 허용하던 디폴트 클래스 필터를 프로퍼티로 받은 클래스 이름을 이용해서 필터를 만들어 덮어씌운다.
    public void setMappedClassName(String mappedClassName){
        this.setClassFilter(new SimpleClassFilter(mappedClassName));
    }

    static class SimpleClassFilter implements ClassFilter{
        String mappedName;

        private SimpleClassFilter(String mappedName) {
            this.mappedName = mappedName;
        }

        @Override
        public boolean matches(Class<?> clazz) {
            // 와일드카드(*)가 들어간 문자열 비교를 지원하는 스프링의 유틸리티 메소드다.
            // *name, name*, *name* 세 가지 방식을 모두 지원한다.
            return PatternMatchUtils.simpleMatch(mappedName, clazz.getSimpleName());
        }
    }
}

Metadata

Metadata

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions