Geçenlerde JSFUnit’i değişikliğe uğratarak WicketTester benzeri bir test altyapısı kurmaya çalıştım. WicketTester,
istemci çağrılarını tek bir noktada ele alıyor. JSFUnit, HtmlUnit, Selenium gibi istemci- sunucu iletişimi söz
konusu olmuyor.
Ancak JSFUnit in-container test framework. Bu nedenle uygulamanın testlerle birlikte sunucuya deploy edilmesini ve
sunucunun çalıştırılmasını bekliyor. HtmlUnit vasıtası ile sunucu tarafında uygulamaya request’ler göndererek
JSFSession ile sayfanın durumunu, FacesContext nesnesini ve dönen cevaptaki html içeriğini test etmeye çalışıyor.
Projelerimizde genellikle UI düzeyinde fonksiyonel testleri gerçekleştirmek için uygulamanın ihtiyaç duyduğu
Spring ApplicationContext’in de initialize olması gerekmektedir. Bu da aslında web container ile uygulamanın ayağa
kaldırılmasına denk gelmektedir. Ayrıca WicketTester’a birebir benzeyen bir test altyapısını oluşturmak için dönen
cevap içerisindeki html sayfasının işlenmesi, web requestlerinin oluşturulması, DOM ağacının kısmi güncellenmesi,
javascript ile ilgili işler gibi konularda kapsamlı çalışma yapmak gerekmektedir. Bütün bu noktaları değerlendirdiğimizde
mevcut çözümlerden JSFUnit, Jetty, JUnit gibi çözümleri bir araya getirerek HTTP tabanlı istemci-sunucu iletişimine
dayanan in-container tabanlı fakat standalone çalıştırılan bir tester oluşturmak daha anlamlı gözüktü.
Geliştirilen JSFTester çözümünün içerisinde JSFUnit’in kullanımı söz konusudur. JSFTester’ da JSFUnit’in
JSFSession nesnesi üzerinden JSFClientSession ve JSFServerSession nesnelerine erişmek gerekiyor. JSFUnit’deki
ClientSession HtmlUnit ile dönen cevabı incelemeye, ServerSession ile de en son request sonucu oluşan FacesContext
nesnesine, UIComponent ağacına ve sunucu tarafındaki “faces managed bean”lara erişmeye yardımcı oluyor. JSFServerSession
nesnesi runtime’da FacesContext’in intialize olmasını bekliyor.
JSFTester içerisinde Jetty web sunucusunu gömülü olarak çalıştırıp HttpSession nesnesini, JSFTester’a `
ServletRequestListener yardımı ile iletmeyi denedim. İletilen Session JSFUnit tarafında bir ThreadLocal değişken
vasıtası ile en son request’e ait FacesContext` nesnesini almak için kullanılıyor.
Bu noktaya kadar başarılı oldum ancak testcase, servlet container’ın classloader’ından farklı bir classloader ile
çalıştırıldığından, JSFServerSession’ın çalışması için gerekli olan faces initialization o classloader’da yapılmamış
oluyordu. Bu durumda dönen cevap üzerinde herhangi bir html elemanına click’lediğimiz vakit JSFUnit tarafında
faces-initialization’a tabi tutulmuş bir thread context olmadığından problem oluyordu. Bunu aşmak içinde Jetty’nin
WebAppContext’inin classLoader’ını testcase’i çalıştıran thread’in contextClassLoader’ı ile değiştirmek gerekiyor.
Daha öncesinde de aynı class’ın Jetty’nin WebAppClassLoader’ı tarafından tekrar yüklenmesi nedeniyle ortaya çıkan bir
class cast probleminden ötürü WebAppContext’in parentLoaderPriority değeri true yapılmıştı. Bu sayede WebAppClassLoader
öncelikle kendisi değil, parent class loader’ı ile sınıfları yüklemeye çalıyordu.
Şu ana kadar karşıma çıkan problemleri bir biçimde çözüme kavuşturdum. Ancak muhtemel en büyük problem noktası olarak
JSFTester’in ve Jetty’nin ayrı ayrı thread’lerde çalışması şimdilik uyuyan bir yanardağ gibi duruyor. Ayrıca HttpSession
timeout olup, yeni bir session yaratıldığı vakit bu session’ın da ThreadLocal değişkene tekrar set edilmesi gerekiyor.