2017-06-21 45 views
0

我面臨的問題是,使用同一個鍵從彈簧緩存方法返回的兩個對象失敗assertSame測試。爲什麼這些對象不共享一個相同的存儲區域?Spring Casheable返回的緩存對象失敗等同性檢查

詳細信息: 我使用redis緩存機制在spring boot REST api中實現緩存。 緩存以它首先從外部提供的源(JPS存儲庫訪問數據庫)中檢索數據的方式正確工作,然後對相同緩存鍵的後續調用將從緩存中返回數據。但是,我無法在JUnit測試用例中完全模仿這種行爲。我的assertEquals或assertSame在緩存返回的2個對象上失敗。

我的代碼庫看起來如下: MVN依賴關係:

<dependency> 
    <groupId>org.springframework.data</groupId> 
    <artifactId>spring-data-redis</artifactId> 
    <version>1.7.6.RELEASE</version> 
</dependency> 
<dependency> 
    <groupId>redis.clients</groupId> 
    <artifactId>jedis</artifactId> 
    <version>2.9.0</version> 
</dependency> 

Spring應用程序配置:

@SpringBootApplication 
@EnableCaching 
public class Application { 
@Value("${redis.host}") 
private String redisHost; 

public static void main(String[] args) { 
    SpringApplication.run(Application.class, args); 
} 

@Bean 
JedisConnectionFactory jedisConnectionFactory() { 
    JedisConnectionFactory jedisConFactory = new JedisConnectionFactory(); 
    jedisConFactory.setHostName(redisHost); 
    jedisConFactory.setPort(6379); 
    return jedisConFactory; 
} 

@Bean 
public RedisTemplate<String, Object> redisTemplate() { 
    RedisTemplate<String, Object> template = new RedisTemplate<String, Object>(); 
    template.setConnectionFactory(jedisConnectionFactory()); 

    return template; 
} 

@Bean 
CacheManager cacheManager() { 
    return new RedisCacheManager(redisTemplate()); 
} 

服務類:

@Service 
public class CIDomainService { 
private RedisTemplate<String, Object> redisTemplate; 
private CIDomainDAO ciDomainDAO; 

@Autowired 
public CIDomainService(CIDomainDAO ciDomainDAO, RedisTemplate<String, Object> redisTemplate) { 
    this.ciDomainDAO = ciDomainDAO; 
    this.redisTemplate = redisTemplate; 
} 

@Cacheable(value = "ciDomain", key = "#id") 
public CIDomain getCIDomain(int id) { 
    CIDomain ciDomain = new CIDomain(); 
    ciDomain.setId(id); 
    ciDomain.setName("SomeName"); 
    return ciDomain; 
} 


public void clearAllCache() { 
    redisTemplate.delete("listCIDomains"); 
    redisTemplate.delete("ciDomain"); 
} 

}

上述服務中的ciDomainDAO只是使用findAll()方法從外部數據庫或內存數據庫檢索數據的JPS存儲庫接口。我的測試類:

@RunWith(SpringJUnit4ClassRunner.class) 
@ActiveProfiles("local") 
@SpringBootTest 
public class CIDomainServiceIntegrationTest { 

@Autowired 
CIDomainService ciDomainServiceSpy; 

@Before 
public void setUp(){ 
    ciDomainServiceSpy.clearAllCache(); 
} 

@Test 
public void listCIDomains_ShouldRetrieveCIDomainsWithCachingPluggedIn() { 
    CIDomain domain1 = ciDomainServiceSpy.getCIDomain(1); 
    CIDomain domain2 = ciDomainServiceSpy.getCIDomain(2); 
    CIDomain domain3 = ciDomainServiceSpy.getCIDomain(1); 
    assertSame(domain1, domain3); //fails 
} 

我的域類:

@Entity 
@Table(name = "CI_DOMAIN") 
public class CIDomain implements Serializable{ 
@Id 
@GeneratedValue(strategy = GenerationType.IDENTITY) 
@Column(name = "id") 
private int id; 

@Column(name = "name") 
private String name; 

public int getId() { 
    return id; 
} 

public void setId(int id) { 
    this.id = id; 
} 

public String getName() { 
    return name; 
} 

public void setName(String name) { 
    this.name = name; 
} 

基於this post據我所知,對象是從庫中檢索的第一個電話,再後來的通話將獲取提供從緩存中該對象提供了相同的「密鑰」。我在上面的測試用例中做了同樣的事情,但assertSame失敗。 Spring緩存必須將緩存對象存儲在爲給定請求獲取的內存中。爲什麼每次發送同一個請求的密鑰都會發送不同的對象。

我試圖有一個替代解決方案,我在服務類中使用間諜並驗證基於相同密鑰請求的方法調用。但是,我遇到了不同的問題。在服務類上創建間諜甚至不使用緩存機制,即使提供了相同的密鑰,也會調用服務getCIDomain方法。我跟着this,this,this,this,this和很多其他的帖子進行進一步的分析,但無法通過assertSame的間諜得到它的工作。

任何幫助真的不勝感激。

+0

,如果他們都相同,則正在檢查('assertSame'(,他們不應該。等於('assertEqual')。相等和相同是不同的事情。對於後者的傳遞,你需要在你的域對象中使用'equals'和'hashCode'方法。 –

+0

@ M.Deinum感謝你的迴應。根據序列化和反序列化「相同」(等於傳遞)java對象h的事實推理您的評論作爲兩個不同的引用或ID,因此object1 == object2將爲false。然而,使用equals而不是assertSame對我來說是有問題的,因爲我認爲「我需要使用assertSame,否則代碼依賴於.equals(Object),它沒有被顯示,可能已被覆蓋比較所有屬性,然後這個測試總是會通過,即使沒有任何東西被緩存。「 – Vishal

+0

同樣不等於。 assertSame不會'domain1 == domain3'這不會是這種情況,因爲它是一個不同的對象實例。你應該真的使用'equals.'而不是別的。如前所述,由您自己來說,緩存(由於使用Redis)會對對象進行序列化和序列化,因此它們將永遠不會相同。即使你使用java序列化,對象也永遠不會相同。相信我,你必須檢查對象的平等,而不是如果他們是相同的(他們永遠不會)。 –

回答

0

我已經解決了這個問題,並能夠設計測試用例,因爲它應該用於驗證彈簧緩存機制。

只是在下面提供我的分析和解決方案,以幫助那些面臨同樣問題的人。

我在上面的評論和原始問題中提到過,assertSame不會工作,因爲序列化的工作原理和assertEquals雖然工作正常,但它有點不能滿足我的測試要求。

我的結論(基於評論)我應該實際測試一些方法調用而不是結果。我試圖模擬CIDomainDAO存儲庫道,因爲在我的問題,但我面臨着幾個問題。創建CIDomainDAO的模擬對象並將其傳遞給CIDomainService構造函數不會觸發Spring緩存,並且我的測試失敗。如果我不嘲笑CIDomainDAO並試圖刺探CIDomainService檢查沒有方法調用的跑到我的測試,然後我結束了越來越

org.mockito.exceptions.misusing.UnfinishedVerificationException: Missing 
method call for verify(mock). 

這是明顯的嘲諷似乎並沒有對最終方法的工作,CIDomainDAO可能在其春天已經生成了JPARepository實現。 This後幫助我理解了mockito的這種行爲。

因此,我需要以某種方式模擬CIDomainDAO,最終我在CIDomainService類中注入了CIDomainDAO存儲庫的模擬版本。我必須在CIDomainService類中專門爲此定義一個CIDomainDAO setter。之後,我嘗試了沒有任何方法調用,並且它按預期工作,即服務調用兩次,但CIDomainDAO調用一次,因爲數據是從第二次調用中的緩存中返回的。

從我上面的原始問題修改的類下方提供。

服務類:

@Service 
public class CIDomainService { 
    private RedisTemplate<String, Object> redisTemplate; 
    private CIDomainDAO ciDomainDAO; 

    @Autowired 
    public CIDomainService(CIDomainDAO ciDomainDAO, RedisTemplate<String, 
    Object> redisTemplate) { 
     this.ciDomainDAO = ciDomainDAO; 
     this.redisTemplate = redisTemplate; 
    } 

    @Cacheable(value = "ciDomain", key = "#id") 
    public CIDomain getCIDomain(int id) { 
     CIDomain ciDomain = new CIDomain(); 
     ciDomain.setId(id); 
     ciDomain.setName("SomeName"); 
     return ciDomain; 
    } 


    public void clearAllCache() { 
     redisTemplate.delete("listCIDomains"); 
     redisTemplate.delete("ciDomain"); 
    } 

    public void setCIDomainDAO(CIDomainDAO ciDomainDAO) { 
     this.ciDomainDAO = ciDomainDAO; 
    } 
} 

這是更新測試用例:

@RunWith(SpringJUnit4ClassRunner.class) 
@ActiveProfiles("local") 
@SpringBootTest 
public class CIDomainServiceIntegrationTest { 

    @Autowired 
    @InjectMocks 
    CIDomainService ciDomainService; 

    @Mock 
    CIDomainDAO ciDomainDAO; 

    @Before 
    public void setUp() { 
     Mockito.reset(ciDomainDAO); 
     ciDomainService.clearAllCache(); 
    } 


    @Test 
    public void listCIDomains_ShouldNotAttemptToCallRepositoryWhenCachingEnabledAfterFirstCallOfRetrievingCIDomains() { 
     List<CIDomain> domains1 = ciDomainService.listCIDomains(); 
     List<CIDomain> domains2 = ciDomainService.listCIDomains(); 
     Mockito.verify(ciDomainDAO, Mockito.times(1)).findAll(); 
    } 

    @Test 
    public void listCIDomains_ShouldAttemptToCallRepositoryWhenCachingIsClearedAfterFirstCallOfRetrievingCIDomains() { 
    List<CIDomain> domains1 = ciDomainService.listCIDomains(); 
     ciDomainService.clearAllCache(); 
     List<CIDomain> domains2 = ciDomainService.listCIDomains(); 
     Mockito.verify(ciDomainDAO, Mockito.times(2)).findAll(); 
    } 

    @After 
    public void postSetUp() { 
     Mockito.validateMockitoUsage(); 
     ciDomainService.clearAllCache(); 
     Mockito.reset(ciDomainDAO); 
     } 
    } 
相關問題