目次

Version 1, last updated by mads379 at Apr 16 15:10 UTC

Unit Testing Snippets With A Logged In User

Thanks to Bill Venners and David Pollak for help with this issue.

Overview

It is common to want to write unit tests for your lift snippets. This page is meant to show you how to test within a context of a session. For instance, say that you have written snippet code that depends upon a user to be logged in. What do you need to do to get the session state enabled so that you have a valid user logged in that you can operate against?

Example code for JUnit

package com.foo
import com.foo.User
import net.liftweb.http.{S, LiftSession}
import net.liftweb.mapper.BaseMetaMapper
import net.liftweb.util._
import net.liftweb.common._
import junit.framework.TestCase

// My snippet probably which lives in another file
class MySnippet {
   def showNames(xhtml : Group) : NodeSeq = {
      val : User = User.currentUser.open_!
      user.children.flatMap(child => bind("f", xhtml, 
                   "name" => child.name
        ))
   }
}

class MySnippetTest  extends TestCase {
   val session : LiftSession = new LiftSession("", StringHelpers.randomString(20), Empty)
   override def setUp : Unit = {
      // set up your db here
   }
   override def tearDown : Unit = {
     // tear down your db here
   }
   private GetNames() = {
      val xml = <xml:group><f:name>Name</f:name></xml:group>
      val snippet = new MySnippet()
      val output = snippet.showNames(xml)
      // Do verification of data returned; assert if something is amiss
      ()
   }
   def testValue() = {
    // Initialize session state if it is not already
    S.initIfUninitted(session) {
      // Create and log-in the user
      val user : User = User.create
      user.firstName("XXX")
      user.lastName("YYYY")
      user.save
      User.logUserIn(user)
      // Call the test to run
      GetNames()
      ()
    }
    ()      
   }
}

Example code for ScalaTest

One way to do this in ScalaTest 1.0 is to override withFixture, which takes a NoArgTest function. ScalaTest will pass each test as a function to withFixture, which is responsible for running the test. The default implementation of withFixture just invokes the test function. If you override withFixture, you can perform setup before invoking the test function, then in a finally clause, perform any needed tear down. Because each test will be executed inside withFixture, you can use withFixture to initialize the session anew for each test using S.initIfUninitted. Here’s an example that uses a FlatSpec:

package com.foo

import com.foo.User
import net.liftweb.http.{S, LiftSession}
import net.liftweb.mapper.BaseMetaMapper
import net.liftweb.util._
import net.liftweb.common._
import org.scalatest.FlatSpec
import org.scalatest.matchers.MustMatchers

// My snippet probably which lives in another file
class MySnippet {
   def showNames(xhtml : Group) : NodeSeq = {
      val : User = User.currentUser.open_!
      user.children.flatMap(child => bind("f", xhtml, 
                   "name" => child.name
        ))
   }
}

class MySnippetSpec extends FlatSpec with MustMatchers {

  val session : LiftSession = new LiftSession("", StringHelpers.randomString(20), Empty)

  override def withFixture(test: NoArgTest) {
    // set up your db here
    try {
      // Initialize session state if it is not already
      S.initIfUninitted(session) {
        // Create and log-in the user
        val user : User = User.create
        user.firstName("XXX")
        user.lastName("YYYY")
        user.save
        User.logUserIn(user)
        test() // Invoke the test function
      }
    }
    finally {
      // tear down your db here
    }
  }

  "My snippet" must "work like I want" in {
    val xml = <xml:group><f:name>Name</f:name></xml:group>
    val snippet = new MySnippet()
    val output = snippet.showNames(xml)
    // Do verification of data returned; assert if something is amiss
    output must not be null // yes, this is valid ScalaTest matcher syntax
  }
}

Example code for Specs

With specs-1.6.1 (available here, user guide there), you can create a SpecContext to start the database before each example, create and log in a user then tear down the database. The code below also specifies that each example will be executed in the context of a lift session with the logged in user.

import com.foo.User
import net.liftweb.http.{S, LiftSession}
import net.liftweb.mapper.BaseMetaMapper
import net.liftweb.util._
import net.liftweb.common._
import org.specs._
import org.specs.specification._

class MySnippetSpec extends Specification with Contexts {
  // run any block of code in a Lift session
  val session = new LiftSession("", StringHelpers.randomString(20), Empty)
  def inSession(a: =>Any) = {
    S.initIfUninitted(session) { a }
  }
  // function to create and log-in a user
  def loginUser = inSession {
    val user: User = User.create
    user.firstName("XXX")
    user.lastName("YYYY")
    user.save
    User.logUserIn(user)
  }
  // specify what to do before/after each example
  // specify that each example must run in the context of a session
  new SpecContext {
    beforeExample {
      /* setup db here */
      loginUser
    }
    afterExample { /* teardown db here */}	
    aroundExpectations(inSession(_))
  }   

  "My snippet works with a user logged in" in {
    val xml = <xml:group><f:name>Name</f:name></xml:group>
    val snippet = new MySnippet()
    val output = snippet.showNames(xml)
    output must not be null
  }
}