在单元测试运行期间禁用某些方面
我有集成测试(加载上下文)和单元测试一起运行。我的代码使用 spring 进行aspectj 编译时编织。
我的问题是我声明的建议也在我的一些单元测试期间运行。这消除了单元测试的概念,这就是为什么我想禁用它们。
是否有一些东西可以放在切入点声明上、一些我可以调用的方法、一些 spring 配置或 Maven 命令来禁用所有 *UnitTest.java 之类的建议?
感谢您的帮助。
示例:
我有以下单元测试:
@RunWith(MockitoJUnitRunner.class)
public class CompanyServiceImplTest {
@Test
public void createCampaignTest() throws Exception {
when(companyDaoMock.saveCompany(any(Campaign.class))).thenReturn(77L);
Long campaignId = companyService.createCampaign(campaignMock);
assertEquals(Long.valueOf(77L), Long.valueOf(campaignId));
}
}
和以下服务方法:
@Override
@Transactional
@EventJournal(type = EventType.CAMPAIGN_CREATE, owner = EventOwner.TERMINAL_USER)
public Long createCampaign(Campaign campaign) {
return companyDao.saveCompany(campaign);
}
方面:
@Aspect
public class EventJournalAspect {
@Autowired
private EventJournalService eventJournalService;
@Pointcut(value="execution(public * *(..))")
public void anyPublicMethod() {}
@Pointcut("within(com.terminal.service..*)")
private void inService() {}
@AfterReturning(pointcut = "anyPublicMethod() && inService() && @annotation(eventJournal) && args(entity,..)", returning = "id")
public void process(Object id, EventJournal eventJournal, AbstractDomainEntity entity)
throws Throwable {
if (eventJournal.type() != EventType.CAMPAIGN_PAYMENT || id != null) {
saveEvent(eventJournal, EventStatus.SUCCESS, entity, (Long) id);
}
}
@AfterThrowing(pointcut = "anyPublicMethod() && inService() && @annotation(eventJournal) && args(entity,..)", throwing="ex")
public void processException(EventJournal eventJournal, AbstractDomainEntity entity, Exception ex) throws Throwable {
saveEvent(eventJournal, EventStatus.FAILURE, entity, null);
}
private void saveEvent(EventJournal eventJournal, EventStatus status, AbstractDomainEntity entity, Long persistentId) {
EventType type = eventJournal.type();
EventOwner owner = eventJournal.owner();
eventJournalService.saveEvent(type, owner, EventStatus.SUCCESS, entity, persistentId);
}
}
测试执行时 - eventJournalService
为 null。因此我看到 NullPointerException
I have integration tests (load context) and unit tests running together. My code does aspectj compile time weaving using spring.
My problem is that my declared advises also run during some of my unit tests. This kills the notion of a unit test, which is why I would like to disable them.
Is there something I can put on the pointcut declaration, some method I can call, some spring configuration, or maven command that disables these advises for something like all *UnitTest.java?
Thanks for the help.
example:
I have the following unit test:
@RunWith(MockitoJUnitRunner.class)
public class CompanyServiceImplTest {
@Test
public void createCampaignTest() throws Exception {
when(companyDaoMock.saveCompany(any(Campaign.class))).thenReturn(77L);
Long campaignId = companyService.createCampaign(campaignMock);
assertEquals(Long.valueOf(77L), Long.valueOf(campaignId));
}
}
and following service method:
@Override
@Transactional
@EventJournal(type = EventType.CAMPAIGN_CREATE, owner = EventOwner.TERMINAL_USER)
public Long createCampaign(Campaign campaign) {
return companyDao.saveCompany(campaign);
}
aspect:
@Aspect
public class EventJournalAspect {
@Autowired
private EventJournalService eventJournalService;
@Pointcut(value="execution(public * *(..))")
public void anyPublicMethod() {}
@Pointcut("within(com.terminal.service..*)")
private void inService() {}
@AfterReturning(pointcut = "anyPublicMethod() && inService() && @annotation(eventJournal) && args(entity,..)", returning = "id")
public void process(Object id, EventJournal eventJournal, AbstractDomainEntity entity)
throws Throwable {
if (eventJournal.type() != EventType.CAMPAIGN_PAYMENT || id != null) {
saveEvent(eventJournal, EventStatus.SUCCESS, entity, (Long) id);
}
}
@AfterThrowing(pointcut = "anyPublicMethod() && inService() && @annotation(eventJournal) && args(entity,..)", throwing="ex")
public void processException(EventJournal eventJournal, AbstractDomainEntity entity, Exception ex) throws Throwable {
saveEvent(eventJournal, EventStatus.FAILURE, entity, null);
}
private void saveEvent(EventJournal eventJournal, EventStatus status, AbstractDomainEntity entity, Long persistentId) {
EventType type = eventJournal.type();
EventOwner owner = eventJournal.owner();
eventJournalService.saveEvent(type, owner, EventStatus.SUCCESS, entity, persistentId);
}
}
When test executes - eventJournalService
is null. Thus I see NullPointerException
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
答案很简单:您想使用
if()
切入点表达式。更新(在问题也更新之后):上面最初提供的链接应该包含足够的信息,但对于它的价值,有一个简短的解释和一个简单的示例:
An
if()
切入点是一个返回布尔值
的静态
方面方法。如果返回值为true
,则表示任何组合切入点如myPointcut() &&只要
就匹配。对于返回值myPointcut()
匹配,if()false
,整个组合切入点不匹配,从而有效地停用连接到切入点的任何建议。那么在静态
if()
切入点中可以做什么呢?如果你想做一些更奇特(和更棘手)的事情并且性能不是那么重要,你还可以尝试动态确定自动连接的方面成员变量是否等于 null ,并且仅在注入对象时激活切入点实际上是存在的。这里唯一的问题是如何从静态方法中确定成员变量。我对Spring AOP一无所知,但在普通的AspectJ中,有一个辅助类
Aspects
,它有几个名为aspectOf(..)
的重载方法。假设您的方面被实例化为单例,您可以执行以下操作:The answer is simple: You want to use an
if()
pointcut expression.Update (after the question has also been updated): The originally provided link above should contain enough information, but for what it is worth, a short explanation and a simple example:
An
if()
pointcut is astatic
aspect method returning aboolean
. If the return value istrue
, it means that any combined pointcut likemyPointcut() && if()
matches as long asmyPointcut()
matches. For a return value offalse
the whole combined pointcut does not match, effectively deactivating any advice connected to the pointcut.So what can you do in a static
if()
pointcut?TestMode.ACTIVE
which is only true during unit or integration testingIf you want to do something fancier (and trickier) and performance is not so important, you can also try to dynamically determine whether the auto-wired aspect member variable equals null or not and only activate your pointcuts if the injected object is actually present. The only problem here is how to determine a member variable from a static method. I have no idea about Spring AOP, but in plain AspectJ there is the helper class
Aspects
with several overloaded methods namedaspectOf(..)
. Assuming that your aspect is instantiated as a singleton, you could do something like this:我只能猜测:
首先要有一个单独的Spring applicationContext-test.xml,
无需组件扫描;
在 Maven 中,您可以添加一个阶段运行时,不包括用于测试的编织 jar。
I can only guess:
The first thing is to have a separate Spring applicationContext-test.xml,
without component-scan;
In maven you can add a phase runtime excluding weaving jars for test.
编译时编织会将建议调用内联到由您拥有的切入点标识的目标方法中。我个人认为,在编译时编织到位的情况下进行单元测试是很好的,因为在运行时您的单元确实包含带有内联建议的类?
我不想包含建议的想法是有两个不同的编译目标,一个有编译时编织,一个没有,你应该能够通过 maven 配置文件来做到这一点,一个不编织建议的 dev 配置文件和一个 prod 配置文件将各个方面编织进去。
Compile time weaving would inline the advice calls in the targeted methods identified by the pointcuts that you have. I personally feel that it is good to unit test with the compile time weaving in place, because at runtime your unit does include the class with the advice inlined?
The thought I have to not include advice would be to have two different compile targets, one with compile time weaving, and one without, you should be able to do this through maven profiles, a dev profile not weaving advice in and a prod profile to weave the aspects in.
您可以编写一个方法,如果当前执行是使用 JUnit 框架启动的,则返回该方法。
该方法可以使用 Thread.currentThread().getStackTrace() 检查堆栈跟踪并搜索 MockitoJUnitRunner 是否存在。
我使用 SpringJUnit4ClassRunner 测试了这个解决方案,但我认为可以与 MockitoJUnitRunner 一起使用。
此外,您还可以拥有一个静态布尔字段,例如:
在项目中(而不是测试中)中存在的类中,并检查控制方法中的值,而不是使用堆栈跟踪。
运行测试时,可以使用 @BeforeClass 注释来设置 TEST_ENVIRONMENT = true。
此解决方案仅为您提供了一种了解代码是否正在测试中运行的方法。
You can write a method that returns if current execution was launched using JUnit framework.
The method can check stack trace with Thread.currentThread().getStackTrace() and search for MockitoJUnitRunner presence.
I tested this solution using a SpringJUnit4ClassRunner but I think could work with MockitoJUnitRunner.
Also, you can have got a static boolean field like:
In a class present in your project (not in your tests) and check the value in the control method instead of use stack trace.
When you run your tests, you can use @BeforeClass annotation to set TEST_ENVIRONMENT = true.
This solution only gives you a way to know if your code is running from a test or not.