MethodLibraryTest: Implemented an alternative approach that reduces duplicated code and may lead to cleaner tests.

--
MOS_MIGRATED_REVID=97326780
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/EvaluationTest.java b/src/test/java/com/google/devtools/build/lib/syntax/EvaluationTest.java
index b764413..9b0ce0b 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/EvaluationTest.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/EvaluationTest.java
@@ -20,8 +20,7 @@
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
-import com.google.devtools.build.lib.packages.PackageFactory;
-import com.google.devtools.build.lib.testutil.TestRuleClassProvider;
+import com.google.devtools.build.lib.testutil.TestMode;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -37,151 +36,161 @@
  */
 @RunWith(JUnit4.class)
 public class EvaluationTest extends EvaluationTestCase {
+  public EvaluationTest() throws Exception {
+    super.setMode(TestMode.BUILD);
+  }
 
-  @Override
-  public EvaluationContext newEvaluationContext() {
-    return EvaluationContext.newBuildContext(getEventHandler(),
-        new PackageFactory(TestRuleClassProvider.getRuleClassProvider()).getEnvironment());
+  /**
+   * Creates a new instance of {@code ModalTestCase}.
+   *
+   * <p>If a test uses this method, it allows potential subclasses to run the very same test in a
+   * different mode in subclasses
+   */
+  protected ModalTestCase newTest() {
+    return new BuildTest();
+    
   }
 
   @Test
   public void testExprs() throws Exception {
-    assertEquals("fooxbar", eval("'%sx' % 'foo' + 'bar'"));
-    assertEquals("fooxbar", eval("('%sx' % 'foo') + 'bar'"));
-    assertEquals("foobarx", eval("'%sx' % ('foo' + 'bar')"));
-    assertEquals(579, eval("123 + 456"));
-    assertEquals(333, eval("456 - 123"));
-    assertEquals(2, eval("8 % 3"));
-
-    checkEvalErrorContains("unsupported operand type(s) for %: 'int' and 'string'", "3 % 'foo'");
+    newTest()
+        .testStatement("'%sx' % 'foo' + 'bar'", "fooxbar")
+        .testStatement("('%sx' % 'foo') + 'bar'", "fooxbar")
+        .testStatement("'%sx' % ('foo' + 'bar')", "foobarx")
+        .testStatement("123 + 456", 579)
+        .testStatement("456 - 123", 333)
+        .testStatement("8 % 3", 2)
+        .testIfErrorContains("unsupported operand type(s) for %: 'int' and 'string'", "3 % 'foo'");
   }
 
-  @SuppressWarnings("unchecked")
   @Test
   public void testListExprs() throws Exception {
-    assertThat((Iterable<Object>) eval("[1, 2, 3]")).containsExactly(1, 2, 3).inOrder();
-    assertThat((Iterable<Object>) eval("(1, 2, 3)")).containsExactly(1, 2, 3).inOrder();
+    newTest().testExactOrder("[1, 2, 3]", 1, 2, 3).testExactOrder("(1, 2, 3)", 1, 2, 3);
   }
 
   @Test
   public void testStringFormatMultipleArgs() throws Exception {
-    assertEquals("XYZ", eval("'%sY%s' % ('X', 'Z')"));
+    newTest().testStatement("'%sY%s' % ('X', 'Z')", "XYZ");
   }
 
   @Test
   public void testAndOr() throws Exception {
-    assertEquals(8, eval("8 or 9"));
-    assertEquals(9, eval("0 or 9"));
-    assertEquals(9, eval("8 and 9"));
-    assertEquals(0, eval("0 and 9"));
+    new BuildTest()
+        .testStatement("8 or 9", 8)
+        .testStatement("0 or 9", 9)
+        .testStatement("8 and 9", 9)
+        .testStatement("0 and 9", 0)
 
-    assertEquals(2, eval("1 and 2 or 3"));
-    assertEquals(3, eval("0 and 2 or 3"));
-    assertEquals(3, eval("1 and 0 or 3"));
+        .testStatement("1 and 2 or 3", 2)
+        .testStatement("0 and 2 or 3", 3)
+        .testStatement("1 and 0 or 3", 3)
 
-    assertEquals(1, eval("1 or 2 and 3"));
-    assertEquals(3, eval("0 or 2 and 3"));
-    assertEquals(0, eval("0 or 0 and 3"));
-    assertEquals(1, eval("1 or 0 and 3"));
-    assertEquals(1, eval("1 or 0 and 3"));
+        .testStatement("1 or 2 and 3", 1)
+        .testStatement("0 or 2 and 3", 3)
+        .testStatement("0 or 0 and 3", 0)
+        .testStatement("1 or 0 and 3", 1)
 
-    assertEquals(Environment.NONE, eval("None and 1"));
-    assertEquals(9, eval("\"\" or 9"));
-    assertEquals("abc", eval("\"abc\" or 9"));
+        .testStatement("None and 1", Environment.NONE)
+        .testStatement("\"\" or 9", 9)
+        .testStatement("\"abc\" or 9", "abc")
+        
+        // check that 'foo' is not evaluated
+        .testStatement("8 or foo", 8)
+        .testStatement("0 and foo", 0);
 
-    if (isSkylark()) {
-      checkEvalError("ERROR 1:6: name 'foo' is not defined", "8 or foo");
-      checkEvalError("ERROR 1:7: name 'foo' is not defined", "0 and foo");
-    } else {
-      assertEquals(8, eval("8 or foo")); // check that 'foo' is not evaluated
-      assertEquals(0, eval("0 and foo")); // check that 'foo' is not evaluated
-    }
+    new SkylarkTest()
+        .testIfErrorContains("name 'google' is not defined", "0 and google")
+        .testIfErrorContains("name 'google' is not defined", "8 or google");
   }
 
   @Test
   public void testNot() throws Exception {
-    assertEquals(false, eval("not 1"));
-    assertEquals(true, eval("not ''"));
+    newTest().testStatement("not 1", false).testStatement("not ''", true);
   }
 
   @Test
   public void testNotWithLogicOperators() throws Exception {
-    assertEquals(true, eval("not (0 and 0)"));
-    assertEquals(false, eval("not (1 or 0)"));
+    newTest()
+        .testStatement("not (0 and 0)", true)
+        .testStatement("not (1 or 0)", false)
 
-    assertEquals(0, eval("0 and not 0"));
-    assertEquals(0, eval("not 0 and 0"));
+        .testStatement("0 and not 0", 0)
+        .testStatement("not 0 and 0", 0)
 
-    assertEquals(true, eval("1 and not 0"));
-    assertEquals(true, eval("not 0 or 0"));
+        .testStatement("1 and not 0", true)
+        .testStatement("not 0 or 0", true)
 
-    assertEquals(0, eval("not 1 or 0"));
-    assertEquals(1, eval("not 1 or 1"));
+        .testStatement("not 1 or 0", 0)
+        .testStatement("not 1 or 1", 1);
   }
 
   @Test
   public void testNotWithArithmeticOperators() throws Exception {
-    assertEquals(true, eval("not 0 + 0"));
-    assertEquals(false, eval("not 2 - 1"));
+    newTest().testStatement("not 0 + 0", true).testStatement("not 2 - 1", false);
   }
 
   @Test
   public void testNotWithCollections() throws Exception {
-    assertEquals(true, eval("not []"));
-    assertEquals(false, eval("not {'a' : 1}"));
+    newTest().testStatement("not []", true).testStatement("not {'a' : 1}", false);
   }
 
   @Test
   public void testEquality() throws Exception {
-    assertEquals(true, eval("1 == 1"));
-    assertEquals(false, eval("1 == 2"));
-    assertEquals(true, eval("'hello' == 'hel' + 'lo'"));
-    assertEquals(false, eval("'hello' == 'bye'"));
-    assertEquals(true, eval("None == None"));
-    assertEquals(true, eval("[1, 2] == [1, 2]"));
-    assertEquals(false, eval("[1, 2] == [2, 1]"));
+    newTest()
+        .testStatement("1 == 1", true)
+        .testStatement("1 == 2", false)
+        .testStatement("'hello' == 'hel' + 'lo'", true)
+        .testStatement("'hello' == 'bye'", false)
+        .testStatement("None == None", true)
+        .testStatement("[1, 2] == [1, 2]", true)
+        .testStatement("[1, 2] == [2, 1]", false);
   }
 
   @Test
   public void testInequality() throws Exception {
-    assertEquals(false, eval("1 != 1"));
-    assertEquals(true, eval("1 != 2"));
-    assertEquals(false, eval("'hello' != 'hel' + 'lo'"));
-    assertEquals(true, eval("'hello' != 'bye'"));
-    assertEquals(false, eval("[1, 2] != [1, 2]"));
-    assertEquals(true, eval("[1, 2] != [2, 1]"));
+    newTest()
+        .testStatement("1 != 1", false)
+        .testStatement("1 != 2", true)
+        .testStatement("'hello' != 'hel' + 'lo'", false)
+        .testStatement("'hello' != 'bye'", true)
+        .testStatement("[1, 2] != [1, 2]", false)
+        .testStatement("[1, 2] != [2, 1]", true);
   }
 
   @Test
   public void testEqualityPrecedence() throws Exception {
-    assertEquals(true, eval("1 + 3 == 2 + 2"));
-    assertEquals(true, eval("not 1 == 2"));
-    assertEquals(false, eval("not 1 != 2"));
-    assertEquals(true, eval("2 and 3 == 3 or 1"));
-    assertEquals(2, eval("2 or 3 == 3 and 1"));
+    newTest()
+        .testStatement("1 + 3 == 2 + 2", true)
+        .testStatement("not 1 == 2", true)
+        .testStatement("not 1 != 2", false)
+        .testStatement("2 and 3 == 3 or 1", true)
+        .testStatement("2 or 3 == 3 and 1", 2);
   }
 
   @Test
   public void testLessThan() throws Exception {
-    assertEquals(true, eval("1 <= 1"));
-    assertEquals(false, eval("1 < 1"));
-    assertEquals(true, eval("'a' <= 'b'"));
-    assertEquals(false, eval("'c' < 'a'"));
+    newTest()
+        .testStatement("1 <= 1", true)
+        .testStatement("1 < 1", false)
+        .testStatement("'a' <= 'b'", true)
+        .testStatement("'c' < 'a'", false);
   }
 
   @Test
   public void testGreaterThan() throws Exception {
-    assertEquals(true, eval("1 >= 1"));
-    assertEquals(false, eval("1 > 1"));
-    assertEquals(false, eval("'a' >= 'b'"));
-    assertEquals(true, eval("'c' > 'a'"));
+    newTest()
+        .testStatement("1 >= 1", true)
+        .testStatement("1 > 1", false)
+        .testStatement("'a' >= 'b'", false)
+        .testStatement("'c' > 'a'", true);
   }
 
   @Test
   public void testConditionalExpressions() throws Exception {
-    assertEquals(1, eval("1 if True else 2"));
-    assertEquals(2, eval("1 if False else 2"));
-    assertEquals(3, eval("1 + 2 if 3 + 4 else 5 + 6"));
+    newTest()
+        .testStatement("1 if True else 2", 1)
+        .testStatement("1 if False else 2", 2)
+        .testStatement("1 + 2 if 3 + 4 else 5 + 6", 3);
 
     setFailFast(false);
     parseExpression("1 if 2");
@@ -191,32 +200,33 @@
 
   @Test
   public void testListComparison() throws Exception {
-    assertThat(eval("[] < [1]")).isEqualTo(true);
-    assertThat(eval("[1] < [1, 1]")).isEqualTo(true);
-    assertThat(eval("[1, 1] < [1, 2]")).isEqualTo(true);
-    assertThat(eval("[1, 2] < [1, 2, 3]")).isEqualTo(true);
-    assertThat(eval("[1, 2, 3] <= [1, 2, 3]")).isEqualTo(true);
+    newTest()
+        .testStatement("[] < [1]", true)
+        .testStatement("[1] < [1, 1]", true)
+        .testStatement("[1, 1] < [1, 2]", true)
+        .testStatement("[1, 2] < [1, 2, 3]", true)
+        .testStatement("[1, 2, 3] <= [1, 2, 3]", true)
 
-    assertThat(eval("['a', 'b'] > ['a']")).isEqualTo(true);
-    assertThat(eval("['a', 'b'] >= ['a']")).isEqualTo(true);
-    assertThat(eval("['a', 'b'] < ['a']")).isEqualTo(false);
-    assertThat(eval("['a', 'b'] <= ['a']")).isEqualTo(false);
+        .testStatement("['a', 'b'] > ['a']", true)
+        .testStatement("['a', 'b'] >= ['a']", true)
+        .testStatement("['a', 'b'] < ['a']", false)
+        .testStatement("['a', 'b'] <= ['a']", false)
 
-    assertThat(eval("('a', 'b') > ('a', 'b')")).isEqualTo(false);
-    assertThat(eval("('a', 'b') >= ('a', 'b')")).isEqualTo(true);
-    assertThat(eval("('a', 'b') < ('a', 'b')")).isEqualTo(false);
-    assertThat(eval("('a', 'b') <= ('a', 'b')")).isEqualTo(true);
+        .testStatement("('a', 'b') > ('a', 'b')", false)
+        .testStatement("('a', 'b') >= ('a', 'b')", true)
+        .testStatement("('a', 'b') < ('a', 'b')", false)
+        .testStatement("('a', 'b') <= ('a', 'b')", true)
 
-    assertThat(eval("[[1, 1]] > [[1, 1], []]")).isEqualTo(false);
-    assertThat(eval("[[1, 1]] < [[1, 1], []]")).isEqualTo(true);
+        .testStatement("[[1, 1]] > [[1, 1], []]", false)
+        .testStatement("[[1, 1]] < [[1, 1], []]", true)
 
-    checkEvalError("Cannot compare int with string", "[1] < ['a']");
-    checkEvalError("Cannot compare list with int", "[1] < 1");
+        .testIfExactError("Cannot compare int with string", "[1] < ['a']")
+        .testIfExactError("Cannot compare list with int", "[1] < 1");
   }
 
   @Test
   public void testCompareStringInt() throws Exception {
-    checkEvalError("Cannot compare string with int", "'a' >= 1");
+    newTest().testIfExactError("Cannot compare string with int", "'a' >= 1");
   }
 
   @Test
@@ -233,18 +243,15 @@
       }
     };
 
-    update(sum.getName(), sum);
-    assertEquals(21, eval("sum(1, 2, 3, 4, 5, 6)"));
-    assertEquals(sum, eval("sum"));
-    assertEquals(0, eval("sum(a=1, b=2)"));
+    newTest().update(sum.getName(), sum).testStatement("sum(1, 2, 3, 4, 5, 6)", 21)
+        .testStatement("sum", sum).testStatement("sum(a=1, b=2)", 0);
   }
 
   @Test
   public void testNotCallInt() throws Exception {
-    eval("sum = 123456");
-    assertEquals(123456, lookup("sum"));
-    checkEvalError("'int' object is not callable", "sum(1, 2, 3, 4, 5, 6)");
-    assertEquals(123456, eval("sum"));
+    newTest().setUp("sum = 123456").testLookup("sum", 123456)
+        .testIfExactError("'int' object is not callable", "sum(1, 2, 3, 4, 5, 6)")
+        .testStatement("sum", 123456);
   }
 
   @Test
@@ -261,59 +268,64 @@
       }
     };
 
-    update(kwargs.getName(), kwargs);
-
-    assertEquals(eval("[('bar', 'bar'), ('foo', 1), ('wiz', [1, 2, 3])]"),
-        eval("kwargs(foo=1, bar='bar', wiz=[1,2,3]).items()"));
+    newTest()
+        .update(kwargs.getName(), kwargs)
+        .testEval(
+            "kwargs(foo=1, bar='bar', wiz=[1,2,3]).items()",
+            "[('bar', 'bar'), ('foo', 1), ('wiz', [1, 2, 3])]");
   }
 
   @Test
   public void testModulo() throws Exception {
-    assertThat(eval("6 % 2")).isEqualTo(0);
-    assertThat(eval("6 % 4")).isEqualTo(2);
-    assertThat(eval("3 % 6")).isEqualTo(3);
-    assertThat(eval("7 % -4")).isEqualTo(-1);
-    assertThat(eval("-7 % 4")).isEqualTo(1);
-    assertThat(eval("-7 % -4")).isEqualTo(-3);
-    checkEvalError("integer modulo by zero", "5 % 0");
+    newTest()
+        .testStatement("6 % 2", 0)
+        .testStatement("6 % 4", 2)
+        .testStatement("3 % 6", 3)
+        .testStatement("7 % -4", -1)
+        .testStatement("-7 % 4", 1)
+        .testStatement("-7 % -4", -3)
+        .testIfExactError("integer modulo by zero", "5 % 0");
   }
 
   @Test
   public void testMult() throws Exception {
-    assertEquals(42, eval("6 * 7"));
-
-    assertEquals("ababab", eval("3 * 'ab'"));
-    assertEquals("", eval("0 * 'ab'"));
-    assertEquals("100000", eval("'1' + '0' * 5"));
+    newTest()
+        .testStatement("6 * 7", 42)
+        .testStatement("3 * 'ab'", "ababab")
+        .testStatement("0 * 'ab'", "")
+        .testStatement("'1' + '0' * 5", "100000");
   }
 
   @Test
   public void testDivision() throws Exception {
-    assertThat(eval("6 / 2")).isEqualTo(3);
-    assertThat(eval("6 / 4")).isEqualTo(1);
-    assertThat(eval("3 / 6")).isEqualTo(0);
-    assertThat(eval("7 / -2")).isEqualTo(-4);
-    assertThat(eval("-7 / 2")).isEqualTo(-4);
-    assertThat(eval("-7 / -2")).isEqualTo(3);
-    assertThat(eval("2147483647 / 2")).isEqualTo(1073741823);
-    checkEvalError("integer division by zero", "5 / 0");
+    newTest()
+        .testStatement("6 / 2", 3)
+        .testStatement("6 / 4", 1)
+        .testStatement("3 / 6", 0)
+        .testStatement("7 / -2", -4)
+        .testStatement("-7 / 2", -4)
+        .testStatement("-7 / -2", 3)
+        .testStatement("2147483647 / 2", 1073741823)
+        .testIfExactError("integer division by zero", "5 / 0");
   }
 
   @Test
   public void testOperatorPrecedence() throws Exception {
-    assertThat(eval("2 + 3 * 4")).isEqualTo(14);
-    assertThat(eval("2 + 3 / 4")).isEqualTo(2);
-    assertThat(eval("2 * 3 + 4 / -2")).isEqualTo(4);
+    newTest()
+        .testStatement("2 + 3 * 4", 14)
+        .testStatement("2 + 3 / 4", 2)
+        .testStatement("2 * 3 + 4 / -2", 4);
   }
 
   @Test
   public void testConcatStrings() throws Exception {
-    assertEquals("foobar", eval("'foo' + 'bar'"));
+    newTest().testStatement("'foo' + 'bar'", "foobar");
   }
 
   @SuppressWarnings("unchecked")
   @Test
   public void testConcatLists() throws Exception {
+    // TODO(fwe): cannot be handled by current testing suite
     // list
     Object x = eval("[1,2] + [3,4]");
     assertThat((Iterable<Object>) x).containsExactly(1, 2, 3, 4).inOrder();
@@ -329,160 +341,150 @@
         "(1,2) + [3,4]"); // list + tuple
   }
 
-  @SuppressWarnings("unchecked")
   @Test
   public void testListComprehensions() throws Exception {
-    assertThat((Iterable<Object>) eval("['foo/%s.java' % x for x in []]")).isEmpty();
-
-    assertThat((Iterable<Object>) eval("['foo/%s.java' % y for y in ['bar', 'wiz', 'quux']]"))
-        .containsExactly("foo/bar.java", "foo/wiz.java", "foo/quux.java").inOrder();
-
-    assertThat((Iterable<Object>) eval(
-        "['%s/%s.java' % (z, t) "
-        + "for z in ['foo', 'bar'] "
-        + "for t in ['baz', 'wiz', 'quux']]"))
-        .containsExactly("foo/baz.java", "foo/wiz.java", "foo/quux.java",
-            "bar/baz.java", "bar/wiz.java", "bar/quux.java").inOrder();
-
-    assertThat((Iterable<Object>) eval(
-        "['%s/%s.java' % (b, b) "
-        + "for a in ['foo', 'bar'] "
-        + "for b in ['baz', 'wiz', 'quux']]"))
-        .containsExactly("baz/baz.java", "wiz/wiz.java", "quux/quux.java",
-            "baz/baz.java", "wiz/wiz.java", "quux/quux.java").inOrder();
-
-    assertThat((Iterable<Object>) eval(
-        "['%s/%s.%s' % (c, d, e) "
-        + "for c in ['foo', 'bar'] "
-        + "for d in ['baz', 'wiz', 'quux'] "
-        + "for e in ['java', 'cc']]"))
-        .containsExactly("foo/baz.java", "foo/baz.cc", "foo/wiz.java", "foo/wiz.cc",
-            "foo/quux.java", "foo/quux.cc", "bar/baz.java", "bar/baz.cc",
-            "bar/wiz.java", "bar/wiz.cc", "bar/quux.java", "bar/quux.cc").inOrder();
+    newTest()
+        .testExactOrder("['foo/%s.java' % x for x in []]")
+        .testExactOrder("['foo/%s.java' % y for y in ['bar', 'wiz', 'quux']]", "foo/bar.java",
+            "foo/wiz.java", "foo/quux.java")
+        .testExactOrder("['%s/%s.java' % (z, t) " + "for z in ['foo', 'bar'] "
+            + "for t in ['baz', 'wiz', 'quux']]",
+            "foo/baz.java",
+            "foo/wiz.java",
+            "foo/quux.java",
+            "bar/baz.java",
+            "bar/wiz.java",
+            "bar/quux.java")
+        .testExactOrder("['%s/%s.java' % (b, b) " + "for a in ['foo', 'bar'] "
+            + "for b in ['baz', 'wiz', 'quux']]",
+            "baz/baz.java",
+            "wiz/wiz.java",
+            "quux/quux.java",
+            "baz/baz.java",
+            "wiz/wiz.java",
+            "quux/quux.java")
+        .testExactOrder("['%s/%s.%s' % (c, d, e) " + "for c in ['foo', 'bar'] "
+            + "for d in ['baz', 'wiz', 'quux'] " + "for e in ['java', 'cc']]",
+            "foo/baz.java",
+            "foo/baz.cc",
+            "foo/wiz.java",
+            "foo/wiz.cc",
+            "foo/quux.java",
+            "foo/quux.cc",
+            "bar/baz.java",
+            "bar/baz.cc",
+            "bar/wiz.java",
+            "bar/wiz.cc",
+            "bar/quux.java",
+            "bar/quux.cc");
   }
 
   @Test
   public void testNestedListComprehensions() throws Exception {
-    assertThat((Iterable<?>) eval(
-          "li = [[1, 2], [3, 4]]\n"
-          + "[j for i in li for j in i]"))
-        .containsExactly(1, 2, 3, 4).inOrder();
-
-    assertThat((Iterable<?>) eval(
-          "input = [['abc'], ['def', 'ghi']]\n"
-          + "['%s %s' % (b, c) for a in input for b in a for c in b]"))
-        .containsExactly(
-            "abc a", "abc b", "abc c", "def d", "def e", "def f", "ghi g", "ghi h", "ghi i")
-        .inOrder();
+    newTest().testExactOrder("li = [[1, 2], [3, 4]]\n" + "[j for i in li for j in i]", 1, 2,
+        3, 4).testExactOrder("input = [['abc'], ['def', 'ghi']]\n"
+        + "['%s %s' % (b, c) for a in input for b in a for c in b]",
+        "abc a",
+        "abc b",
+        "abc c",
+        "def d",
+        "def e",
+        "def f",
+        "ghi g",
+        "ghi h",
+        "ghi i");
   }
 
   @Test
   public void testListComprehensionsMultipleVariables() throws Exception {
-    assertThat(eval("[x + y for x, y in [(1, 2), (3, 4)]]").toString())
-        .isEqualTo("[3, 7]");
-    assertThat(eval("[z + t for (z, t) in [[1, 2], [3, 4]]]").toString())
-        .isEqualTo("[3, 7]");
+    newTest().testEval("[x + y for x, y in [(1, 2), (3, 4)]]", "[3, 7]").testEval(
+        "[z + t for (z, t) in [[1, 2], [3, 4]]]", "[3, 7]");
   }
 
   @Test
   public void testListComprehensionsMultipleVariablesFail() throws Exception {
-    checkEvalError("lvalue has length 3, but rvalue has has length 2",
-        "[x + y for x, y, z in [(1, 2), (3, 4)]]");
-
-    checkEvalError("type 'int' is not a collection",
-        "[x + y for x, y in (1, 2)]");
+    newTest().testIfExactError("lvalue has length 3, but rvalue has has length 2",
+        "[x + y for x, y, z in [(1, 2), (3, 4)]]").testIfExactError(
+        "type 'int' is not a collection", "[x + y for x, y in (1, 2)]");
   }
 
   @Test
   public void testListComprehensionsWithFiltering() throws Exception {
-    eval("range3 = [0, 1, 2]"); // used below
-
-    assertThat(eval("[a for a in (4, None, 2, None, 1) if a != None]").toString())
-        .isEqualTo("[4, 2, 1]");
-    assertThat(eval("[b+c for b in [0, 1, 2] for c in [0, 1, 2] if b + c > 2]").toString())
-        .isEqualTo("[3, 3, 4]");
-    assertThat(eval("[d+e for d in range3 if d % 2 == 1 for e in range3]").toString())
-        .isEqualTo("[1, 2, 3]");
-    assertThat(eval(
-        "[[f,g] for f in [0, 1, 2, 3, 4] if f for g in [5, 6, 7, 8] if f * g % 12 == 0 ]")
-        .toString()).isEqualTo("[[2, 6], [3, 8], [4, 6]]");
-    assertThat(eval("[h for h in [4, 2, 0, 1] if h]").toString())
-        .isEqualTo("[4, 2, 1]");
+    newTest()
+        .setUp("range3 = [0, 1, 2]")
+        .testEval("[a for a in (4, None, 2, None, 1) if a != None]", "[4, 2, 1]")
+        .testEval("[b+c for b in [0, 1, 2] for c in [0, 1, 2] if b + c > 2]", "[3, 3, 4]")
+        .testEval("[d+e for d in range3 if d % 2 == 1 for e in range3]", "[1, 2, 3]")
+        .testEval("[[f,g] for f in [0, 1, 2, 3, 4] if f for g in [5, 6, 7, 8] if f * g % 12 == 0 ]",
+            "[[2, 6], [3, 8], [4, 6]]")
+        .testEval("[h for h in [4, 2, 0, 1] if h]", "[4, 2, 1]");
   }
 
   @Test
   public void testListComprehensionDefinitionOrder() throws Exception {
-    checkEvalErrorContains("name 'y' is not defined",
+    newTest().testIfErrorContains("name 'y' is not defined",
         "[x for x in (1, 2) if y for y in (3, 4)]");
   }
 
   @Test
   public void testTupleDestructuring() throws Exception {
-    eval("a, b = 1, 2");
-    assertThat(lookup("a")).isEqualTo(1);
-    assertThat(lookup("b")).isEqualTo(2);
-
-    eval("c, d = {'key1':2, 'key2':3}");
-    assertThat(lookup("c")).isEqualTo("key1");
-    assertThat(lookup("d")).isEqualTo("key2");
+    newTest()
+        .setUp("a, b = 1, 2")
+        .testLookup("a", 1)
+        .testLookup("b", 2)
+        .setUp("c, d = {'key1':2, 'key2':3}")
+        .testLookup("c", "key1")
+        .testLookup("d", "key2");
   }
 
   @Test
   public void testHeterogeneousDict() throws Exception {
-    eval("d = {'str': 1, 2: 3}\n"
-         + "a = d['str']\n"
-         + "b = d[2]");
-    assertThat(lookup("a")).isEqualTo(1);
-    assertThat(lookup("b")).isEqualTo(3);
+    newTest().setUp("d = {'str': 1, 2: 3}", "a = d['str']", "b = d[2]").testLookup("a", 1)
+        .testLookup("b", 3);
   }
 
   @Test
   public void testRecursiveTupleDestructuring() throws Exception {
-    eval("((a, b), (c, d)) = [(1, 2), (3, 4)]");
-    assertThat(lookup("a")).isEqualTo(1);
-    assertThat(lookup("b")).isEqualTo(2);
-    assertThat(lookup("c")).isEqualTo(3);
-    assertThat(lookup("d")).isEqualTo(4);
+    newTest()
+        .setUp("((a, b), (c, d)) = [(1, 2), (3, 4)]")
+        .testLookup("a", 1)
+        .testLookup("b", 2)
+        .testLookup("c", 3)
+        .testLookup("d", 4);
   }
 
-  @SuppressWarnings("unchecked")
   @Test
   public void testListComprehensionModifiesGlobalEnv() throws Exception {
-    update("x", 42);
-    if (isSkylark()) {
-      checkEvalError("ERROR 1:1: Variable x is read only", "[x + 1 for x in [1,2,3]]");
-    } else {
-      assertThat((Iterable<Object>) eval("[x + 1 for x in [1,2,3]]"))
-          .containsExactly(2, 3, 4).inOrder();
-      assertEquals(3, lookup("x")); // (x is global)
-    }
+    new SkylarkTest().update("x", 42).testIfExactError("ERROR 1:1: Variable x is read only",
+        "[x + 1 for x in [1,2,3]]");
+    new BuildTest().update("x", 42).setUp("y =[x + 1 for x in [1,2,3]]")
+        .testExactOrder("y", 2, 3, 4).testLookup("x", 3); // (x is global)
   }
 
   @Test
   public void testDictComprehensions() throws Exception {
-    assertEquals(Collections.emptyMap(), eval("{a : a for a in []}"));
-    assertEquals(ImmutableMap.of(1, 1, 2, 2), eval("{b : b for b in [1, 2]}"));
-    assertEquals(ImmutableMap.of("a", "v_a", "b", "v_b"),
-        eval("{c : 'v_' + c for c in ['a', 'b']}"));
-    assertEquals(ImmutableMap.of("k_a", "a", "k_b", "b"),
-        eval("{'k_' + d : d for d in ['a', 'b']}"));
-    assertEquals(ImmutableMap.of("k_a", "v_a", "k_b", "v_b"),
-        eval("{'k_' + e : 'v_' + e for e in ['a', 'b']}"));
-    assertEquals(ImmutableMap.of(5, 6), eval("{x+y : x*y for x, y in [[2, 3]]}"));
+    newTest()
+        .testStatement("{a : a for a in []}", Collections.emptyMap())
+        .testStatement("{b : b for b in [1, 2]}", ImmutableMap.of(1, 1, 2, 2))
+        .testStatement("{c : 'v_' + c for c in ['a', 'b']}",
+            ImmutableMap.of("a", "v_a", "b", "v_b"))
+        .testStatement("{'k_' + d : d for d in ['a', 'b']}",
+            ImmutableMap.of("k_a", "a", "k_b", "b"))
+        .testStatement("{'k_' + e : 'v_' + e for e in ['a', 'b']}",
+            ImmutableMap.of("k_a", "v_a", "k_b", "v_b"))
+        .testStatement("{x+y : x*y for x, y in [[2, 3]]}", ImmutableMap.of(5, 6));
   }
 
   @Test
   public void testDictComprehensionOnNonIterable() throws Exception {
-    checkEvalError(
-        "type 'int' is not iterable",
-        "{k : k for k in 3}");
+    newTest().testIfExactError("type 'int' is not iterable", "{k : k for k in 3}");
   }
 
   @Test
   public void testDictComprehensions_MultipleKey() throws Exception {
-    assertEquals(ImmutableMap.of(1, 1, 2, 2), eval("{x : x for x in [1, 2, 1]}"));
-    assertEquals(ImmutableMap.of("ab", "ab", "c", "c"),
-        eval("{y : y for y in ['ab', 'c', 'a' + 'b']}"));
+    newTest().testStatement("{x : x for x in [1, 2, 1]}", ImmutableMap.of(1, 1, 2, 2))
+        .testStatement("{y : y for y in ['ab', 'c', 'a' + 'b']}",
+            ImmutableMap.of("ab", "ab", "c", "c"));
   }
 
   @Test
@@ -495,92 +497,92 @@
 
   @Test
   public void testListConcatenation() throws Exception {
-    assertEquals(Arrays.asList(1, 2, 3, 4), eval("[1, 2] + [3, 4]"));
-    assertEquals(ImmutableList.of(1, 2, 3, 4), eval("(1, 2) + (3, 4)"));
-    checkEvalError("can only concatenate Tuple (not \"List\") to Tuple",
-        "[1, 2] + (3, 4)");
-    checkEvalError("can only concatenate List (not \"Tuple\") to List",
-        "(1, 2) + [3, 4]");
+    newTest()
+        .testStatement("[1, 2] + [3, 4]", Arrays.asList(1, 2, 3, 4))
+        .testStatement("(1, 2) + (3, 4)", ImmutableList.of(1, 2, 3, 4))
+        .testIfExactError("can only concatenate Tuple (not \"List\") to Tuple", "[1, 2] + (3, 4)")
+        .testIfExactError("can only concatenate List (not \"Tuple\") to List", "(1, 2) + [3, 4]");
   }
 
   @SuppressWarnings("unchecked")
   @Test
   public void testSelectorListConcatenation() throws Exception {
+    // TODO(fwe): cannot be handled by current testing suite
     SelectorList x = (SelectorList) eval("select({'foo': ['FOO'], 'bar': ['BAR']}) + []");
     List<Object> elements = x.getElements();
     assertThat(elements.size()).isEqualTo(2);
     assertThat(elements.get(0)).isInstanceOf(SelectorValue.class);
-    assertThat((Iterable) elements.get(1)).isEmpty();
+    assertThat((Iterable<Object>) elements.get(1)).isEmpty();
   }
 
   @Test
   public void testListComprehensionFailsOnNonSequence() throws Exception {
-    checkEvalErrorContains("type 'int' is not iterable", "[x + 1 for x in 123]");
+    newTest().testIfErrorContains("type 'int' is not iterable", "[x + 1 for x in 123]");
   }
 
-  @SuppressWarnings("unchecked")
   @Test
   public void testListComprehensionOnString() throws Exception {
-    assertThat((Iterable<Object>) eval("[x for x in 'abc']"))
-        .containsExactly("a", "b", "c").inOrder();
+    newTest().testExactOrder("[x for x in 'abc']", "a", "b", "c");
   }
 
   @Test
   public void testInvalidAssignment() throws Exception {
-    update("x", 1);
-    checkEvalErrorContains("can only assign to variables and tuples, not to 'x + 1'",
-        "x + 1 = 2");
+    newTest().testIfErrorContains(
+        "can only assign to variables and tuples, not to 'x + 1'", "x + 1 = 2");
   }
 
-  @SuppressWarnings("unchecked")
   @Test
   public void testListComprehensionOnDictionary() throws Exception {
-    assertThat((Iterable<Object>) eval("val = ['var_' + n for n in {'a':1,'b':2}] ; val"))
-        .containsExactly("var_a", "var_b").inOrder();
+    newTest().testExactOrder("val = ['var_' + n for n in {'a':1,'b':2}] ; val", "var_a", "var_b");
   }
 
   @Test
   public void testListComprehensionOnDictionaryCompositeExpression() throws Exception {
-    eval("d = {1:'a',2:'b'}\n" + "l = [d[x] for x in d]");
-    assertEquals("[\"a\", \"b\"]", Printer.repr(lookup("l")));
+    new BuildTest()
+        .setUp("d = {1:'a',2:'b'}", "l = [d[x] for x in d]")
+        .testLookup("l", ImmutableList.of("a", "b"));
   }
 
   @Test
   public void testInOperator() throws Exception {
-    assertEquals(Boolean.TRUE, eval("'b' in ['a', 'b']"));
-    assertEquals(Boolean.FALSE, eval("'c' in ['a', 'b']"));
-    assertEquals(Boolean.TRUE, eval("'b' in ('a', 'b')"));
-    assertEquals(Boolean.FALSE, eval("'c' in ('a', 'b')"));
-    assertEquals(Boolean.TRUE, eval("'b' in {'a' : 1, 'b' : 2}"));
-    assertEquals(Boolean.FALSE, eval("'c' in {'a' : 1, 'b' : 2}"));
-    assertEquals(Boolean.FALSE, eval("1 in {'a' : 1, 'b' : 2}"));
-    assertEquals(Boolean.TRUE, eval("'b' in 'abc'"));
-    assertEquals(Boolean.FALSE, eval("'d' in 'abc'"));
+    newTest()
+        .testStatement("'b' in ['a', 'b']", Boolean.TRUE)
+        .testStatement("'c' in ['a', 'b']", Boolean.FALSE)
+        .testStatement("'b' in ('a', 'b')", Boolean.TRUE)
+        .testStatement("'c' in ('a', 'b')", Boolean.FALSE)
+        .testStatement("'b' in {'a' : 1, 'b' : 2}", Boolean.TRUE)
+        .testStatement("'c' in {'a' : 1, 'b' : 2}", Boolean.FALSE)
+        .testStatement("1 in {'a' : 1, 'b' : 2}", Boolean.FALSE)
+        .testStatement("'b' in 'abc'", Boolean.TRUE)
+        .testStatement("'d' in 'abc'", Boolean.FALSE);
   }
 
   @Test
   public void testNotInOperator() throws Exception {
-    assertEquals(Boolean.FALSE, eval("'b' not in ['a', 'b']"));
-    assertEquals(Boolean.TRUE, eval("'c' not in ['a', 'b']"));
-    assertEquals(Boolean.FALSE, eval("'b' not in ('a', 'b')"));
-    assertEquals(Boolean.TRUE, eval("'c' not in ('a', 'b')"));
-    assertEquals(Boolean.FALSE, eval("'b' not in {'a' : 1, 'b' : 2}"));
-    assertEquals(Boolean.TRUE, eval("'c' not in {'a' : 1, 'b' : 2}"));
-    assertEquals(Boolean.TRUE, eval("1 not in {'a' : 1, 'b' : 2}"));
-    assertEquals(Boolean.FALSE, eval("'b' not in 'abc'"));
-    assertEquals(Boolean.TRUE, eval("'d' not in 'abc'"));
+    newTest()
+        .testStatement("'b' not in ['a', 'b']", Boolean.FALSE)
+        .testStatement("'c' not in ['a', 'b']", Boolean.TRUE)
+        .testStatement("'b' not in ('a', 'b')", Boolean.FALSE)
+        .testStatement("'c' not in ('a', 'b')", Boolean.TRUE)
+        .testStatement("'b' not in {'a' : 1, 'b' : 2}", Boolean.FALSE)
+        .testStatement("'c' not in {'a' : 1, 'b' : 2}", Boolean.TRUE)
+        .testStatement("1 not in {'a' : 1, 'b' : 2}", Boolean.TRUE)
+        .testStatement("'b' not in 'abc'", Boolean.FALSE)
+        .testStatement("'d' not in 'abc'", Boolean.TRUE);
   }
 
   @Test
   public void testInFail() throws Exception {
-    checkEvalError("in operator only works on strings if the left operand is also a string",
-        "1 in '123'");
-    checkEvalError("in operator only works on lists, tuples, sets, dicts and strings", "'a' in 1");
+    newTest()
+        .testIfExactError(
+            "in operator only works on strings if the left operand is also a string", "1 in '123'")
+        .testIfExactError(
+            "in operator only works on lists, tuples, sets, dicts and strings", "'a' in 1");
   }
 
   @Test
   public void testInCompositeForPrecedence() throws Exception {
-    assertEquals(0, eval("not 'a' in ['a'] or 0"));
+    newTest().testStatement("not 'a' in ['a'] or 0", 0);
   }
 
   private Object createObjWithStr() {
@@ -594,50 +596,48 @@
 
   @Test
   public void testPercOnObject() throws Exception {
-    update("obj", createObjWithStr());
-    assertEquals("str marker", eval("'%s' % obj"));
+    newTest().update("obj", createObjWithStr()).testStatement("'%s' % obj", "str marker");
   }
 
   @Test
   public void testPercOnObjectList() throws Exception {
-    update("obj", createObjWithStr());
-    assertEquals("str marker str marker", eval("'%s %s' % (obj, obj)"));
+    newTest().update("obj", createObjWithStr()).testStatement("'%s %s' % (obj, obj)",
+        "str marker str marker");
   }
 
   @Test
   public void testPercOnObjectInvalidFormat() throws Exception {
-    update("obj", createObjWithStr());
-    checkEvalError("invalid argument str marker for format pattern %d", "'%d' % obj");
+    newTest().update("obj", createObjWithStr()).testIfExactError(
+        "invalid argument str marker for format pattern %d", "'%d' % obj");
   }
 
-  @SuppressWarnings("unchecked")
   @Test
   public void testDictKeys() throws Exception {
-    assertThat((Iterable<Object>) eval("v = {'a': 1}.keys() + ['b', 'c'] ; v"))
-        .containsExactly("a", "b", "c").inOrder();
+    newTest().testExactOrder("v = {'a': 1}.keys() + ['b', 'c'] ; v", "a", "b", "c");
   }
 
   @Test
   public void testDictKeysTooManyArgs() throws Exception {
-    checkEvalError("too many (2) positional arguments in call to keys(self: dict)",
-        "{'a': 1}.keys('abc')");
+    newTest().testIfExactError(
+        "too many (2) positional arguments in call to keys(self: dict)", "{'a': 1}.keys('abc')");
   }
 
   @Test
   public void testDictKeysTooManyKeyArgs() throws Exception {
-    checkEvalError("unexpected keyword 'arg' in call to keys(self: dict)",
+    newTest().testIfExactError("unexpected keyword 'arg' in call to keys(self: dict)",
         "{'a': 1}.keys(arg='abc')");
   }
 
   @Test
   public void testDictKeysDuplicateKeyArgs() throws Exception {
-    checkEvalError("duplicate keywords 'arg', 'k' in call to keys",
+    newTest().testIfExactError("duplicate keywords 'arg', 'k' in call to keys",
         "{'a': 1}.keys(arg='abc', arg='def', k=1, k=2)");
   }
 
   @Test
   public void testArgBothPosKey() throws Exception {
-    checkEvalErrorStartsWith("arguments 'old', 'new' passed both by position and by name "
+    newTest().testIfErrorContains(
+        "arguments 'old', 'new' passed both by position and by name "
         + "in call to replace(self: string, ",
         "'banana'.replace('a', 'o', 3, old='a', new=4)");
   }
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/EvaluationTestCase.java b/src/test/java/com/google/devtools/build/lib/syntax/EvaluationTestCase.java
index 5749c40..692a338 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/EvaluationTestCase.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/EvaluationTestCase.java
@@ -16,43 +16,89 @@
 import static com.google.common.truth.Truth.assertThat;
 import static org.junit.Assert.fail;
 
+import com.google.common.truth.Ordered;
 import com.google.devtools.build.lib.events.Event;
 import com.google.devtools.build.lib.events.EventCollector;
 import com.google.devtools.build.lib.events.EventHandler;
 import com.google.devtools.build.lib.events.EventKind;
 import com.google.devtools.build.lib.events.util.EventCollectionApparatus;
-import com.google.devtools.build.lib.rules.SkylarkModules;
+import com.google.devtools.build.lib.packages.PackageFactory;
+import com.google.devtools.build.lib.testutil.TestMode;
+import com.google.devtools.build.lib.testutil.TestRuleClassProvider;
 
 import org.junit.Before;
 
+import java.util.LinkedList;
 import java.util.List;
 
 /**
  * Base class for test cases that use parsing and evaluation services.
  */
 public class EvaluationTestCase {
-
+  
   private EventCollectionApparatus eventCollectionApparatus;
+  private PackageFactory factory;
+  private TestMode testMode = TestMode.SKYLARK;
+  
   protected EvaluationContext evaluationContext;
 
+  public EvaluationTestCase()   {
+    createNewInfrastructure();
+  }
+  
   @Before
   public void setUp() throws Exception {
-    eventCollectionApparatus = new EventCollectionApparatus(EventKind.ALL_EVENTS);
+    createNewInfrastructure();
     evaluationContext = newEvaluationContext();
   }
 
+  public EvaluationContext newEvaluationContext() throws Exception {
+    if (testMode == null) {
+      throw new IllegalArgumentException(
+          "TestMode is null. Please set a Testmode via setMode() or set the "
+          + "evaluatenContext manually by overriding newEvaluationContext()");
+    }
+
+    return testMode.createContext(getEventHandler(), factory.getEnvironment());
+  }
+
+  protected void createNewInfrastructure()  {
+    eventCollectionApparatus = new EventCollectionApparatus(EventKind.ALL_EVENTS);
+    factory = new PackageFactory(TestRuleClassProvider.getRuleClassProvider());
+  }
+  
+  /**
+   * Sets the specified {@code TestMode} and tries to create the appropriate {@code
+   * EvaluationContext}
+   * 
+   * @param testMode
+   * @throws Exception
+   */
+  protected void setMode(TestMode testMode) throws Exception {
+    this.testMode = testMode;
+    evaluationContext = newEvaluationContext();
+  }
+
+  protected void enableSkylarkMode() throws Exception {
+    setMode(TestMode.SKYLARK);
+  }
+
+  protected void enableBuildMode() throws Exception {
+    setMode(TestMode.BUILD);
+  }
+
   protected EventHandler getEventHandler() {
     return eventCollectionApparatus.reporter();
   }
 
+  protected PackageFactory getFactory() {
+    return factory;
+  }
+
   public Environment getEnvironment() {
     return evaluationContext.getEnvironment();
   }
-
-  public EvaluationContext newEvaluationContext() throws Exception {
-    return SkylarkModules.newEvaluationContext(getEventHandler());
-  }
-
+  
   public boolean isSkylark() {
     return evaluationContext.isSkylark();
   }
@@ -133,4 +179,356 @@
     eventCollectionApparatus.collector().clear();
     return this;
   }
+
+  /**
+   * Encapsulates a separate test which can be executed by a {@code TestMode}
+   */
+  protected interface Testable {
+    public void run() throws Exception;
+  }
+
+  /**
+   * Base class for test cases that run in specific modes (e.g. Build and/or Skylark)
+   *
+   */  
+  protected abstract class ModalTestCase {
+    private final SetupActions setup;
+    
+    protected ModalTestCase()   {
+      setup = new SetupActions();
+    }
+    
+    /**
+     * Allows the execution of several statements before each following test
+     * @param statements The statement(s) to be executed
+     * @return This {@code ModalTestCase}
+     */
+    public ModalTestCase setUp(String... statements) {
+      setup.registerEval(statements);
+      return this;
+    }
+
+    /**
+     * Allows the update of the specified variable before each following test
+     * @param name The name of the variable that should be updated
+     * @param value The new value of the variable
+     * @return This {@code ModalTestCase}
+     */
+    public ModalTestCase update(String name, Object value) {
+      setup.registerUpdate(name, value);
+      return this;
+    }
+    
+    /**
+     * Evaluates two parameters and compares their results.
+     * @param statement The statement to be evaluated
+     * @param expectedEvalString The expression of the expected result
+     * @return This {@code ModalTestCase}
+     * @throws Exception
+     */
+    public ModalTestCase testEval(String statement, String expectedEvalString) throws Exception {
+      runTest(createComparisonTestable(statement, expectedEvalString, true));
+      return this;
+    }
+
+    /**
+     * Evaluates the given statement and compares its result to the expected object
+     * @param statement
+     * @param expected
+     * @return This {@code ModalTestCase}
+     * @throws Exception
+     */
+    public ModalTestCase testStatement(String statement, Object expected) throws Exception {
+      runTest(createComparisonTestable(statement, expected, false));
+      return this;
+    }
+
+    /**
+     * Evaluates the given statement and compares its result to the collection of expected objects
+     * without considering their order
+     * @param statement The statement to be evaluated
+     * @param items The expected items
+     * @return This {@code ModalTestCase}
+     * @throws Exception
+     */
+    public ModalTestCase testCollection(String statement, Object... items) throws Exception {
+      runTest(collectionTestable(statement, false, items));
+      return this;
+    }
+    
+    /**
+     * Evaluates the given statement and compares its result to the collection of expected objects
+     * while considering their order
+     * @param statement The statement to be evaluated
+     * @param items The expected items, in order
+     * @return This {@code ModalTestCase}
+     * @throws Exception
+     */
+    public ModalTestCase testExactOrder(String statement, Object... items) throws Exception {
+      runTest(collectionTestable(statement, true, items));
+      return this;
+    }
+
+    /**
+     * Evaluates the given statement and checks whether the given error message appears
+     * @param expectedError The expected error message
+     * @param statements The statement(s) to be evaluated
+     * @return This ModalTestCase
+     * @throws Exception
+     */
+    public ModalTestCase testIfExactError(String expectedError, String... statements)
+        throws Exception {
+      runTest(errorTestable(true, expectedError, statements));
+      return this;
+    }
+
+    /**
+     * Evaluates the given statement and checks whether an error that contains the expected message
+     * occurs
+     * @param expectedError
+     * @param statements
+     * @return This ModalTestCase
+     * @throws Exception
+     */
+    public ModalTestCase testIfErrorContains(String expectedError, String... statements)
+        throws Exception {
+      runTest(errorTestable(false, expectedError, statements));
+      return this;
+    }
+
+    /**
+     * Looks up the value of the specified variable and compares it to the expected value
+     * @param name
+     * @param expected
+     * @return This ModalTestCase
+     * @throws Exception
+     */
+    public ModalTestCase testLookup(String name, Object expected) throws Exception {
+      runTest(createLookUpTestable(name, expected));
+      return this;
+    }
+
+    /**
+     * Creates a Testable that checks whether the evaluation of the given statement leads to the
+     * expected error
+     * @param statements
+     * @param error
+     * @param exactMatch If true, the error message has to be identical to the expected error
+     * @return An instance of Testable that runs the error check
+     */
+    protected Testable errorTestable(final boolean exactMatch, final String error,
+        final String... statements) {
+      return new Testable() {
+        @Override
+        public void run() throws Exception {
+          if (exactMatch) {
+            checkEvalError(error, statements);
+          } else {
+            checkEvalErrorContains(error, statements);
+          }
+        }
+      };
+    }
+
+    /**
+     * Creates a testable that checks whether the evaluation of the given statement leads to a list
+     * that contains exactly the expected objects
+     * @param statement The statement to be evaluated
+     * @param ordered Determines whether the order of the elements is checked as well
+     * @param expected Expected objects
+     * @return An instance of Testable that runs the check
+     */
+    protected Testable collectionTestable(
+        final String statement, final boolean ordered, final Object... expected) {
+      return new Testable() {
+        @Override
+        public void run() throws Exception {
+          Ordered tmp = assertThat((Iterable<?>) eval(statement)).containsExactly(expected);
+
+          if (ordered) {
+            tmp.inOrder();
+          }
+        }
+      };
+    }
+
+    /**
+     * Creates a testable that compares the evaluation of the given statement to a specified result
+     *
+     * @param statement The statement to be evaluated
+     * @param expected Either the expected object or an expression whose evaluation leads to the
+     *  expected object
+     * @param expectedIsExpression Signals whether {@code expected} is an object or an expression
+     * @return An instance of Testable that runs the comparison
+     */
+    protected Testable createComparisonTestable(
+        final String statement, final Object expected, final boolean expectedIsExpression) {
+      return new Testable() {
+        @Override
+        public void run() throws Exception {
+          Object actual = eval(statement);
+
+          // Prints the actual object instead of evaluating the expected expression
+          if (expectedIsExpression) {
+            actual = Printer.repr(actual, '\'');
+          }
+
+          assertThat(actual).isEqualTo(expected);
+        }
+      };
+    }
+
+    /**
+     * Creates a Testable that looks up the given variable and compares its value to the expected
+     * value
+     * @param name
+     * @param expected
+     * @return An instance of Testable that does both lookup and comparison
+     */
+    protected Testable createLookUpTestable(final String name, final Object expected) {
+      return new Testable() {
+        @Override
+        public void run() throws Exception {
+          assertThat(lookup(name)).isEqualTo(expected);
+        }
+      };
+    }
+
+    /**
+     * Executes the given Testable
+     * @param testable
+     * @throws Exception
+     */
+    protected void runTest(Testable testable) throws Exception {
+      run(new TestableDecorator(setup, testable));
+    }
+
+    protected abstract void run(Testable testable) throws Exception;
+  }
+
+  /**
+   * A simple decorator that allows the execution of setup actions before running 
+   * a {@code Testable}
+   */
+  class TestableDecorator implements Testable {
+    private final SetupActions setup;
+    private final Testable decorated;
+
+    public TestableDecorator(SetupActions setup, Testable decorated) {
+      this.setup = setup;
+      this.decorated = decorated;
+    }
+
+    /**
+     * Executes all stored actions and updates plus the actual {@code Testable}
+     */
+    @Override
+    public void run() throws Exception {
+      setup.executeAll();
+      decorated.run();
+    }
+  }
+
+  /**
+   * A container for collection actions that should be executed before a test
+   */
+  class SetupActions {
+    private List<Testable> setup;
+
+    public SetupActions() {
+      setup = new LinkedList<>();
+    }
+
+    /**
+     * Registers a variable that has to be updated before a test
+     *
+     * @param name
+     * @param value
+     */
+    public void registerUpdate(final String name, final Object value) {
+      setup.add(new Testable() {
+        @Override
+        public void run() throws Exception {
+          EvaluationTestCase.this.update(name, value);
+        }
+      });
+    }
+
+    /**
+     * Registers a statement for evaluation prior to a test
+     *
+     * @param statements
+     */
+    public void registerEval(final String... statements) {
+      setup.add(new Testable() {
+        @Override
+        public void run() throws Exception {
+          EvaluationTestCase.this.eval(statements);
+        }
+      });
+    }
+
+    /**
+     * Executes all stored actions and updates
+     * @throws Exception
+     */
+    public void executeAll() throws Exception {
+      for (Testable testable : setup) {
+        testable.run();
+      }
+    }
+  }
+    
+  /**
+   * A class that executes each separate test in both modes (Build and Skylark)
+   */
+  protected class BothModesTest extends ModalTestCase {
+    public BothModesTest() {}
+
+    /**
+     * Executes the given Testable in both Build and Skylark mode
+     */
+    @Override
+    protected void run(Testable testable) throws Exception {
+      enableSkylarkMode();
+      try {
+        testable.run();
+      } catch (Exception e) {
+        throw new Exception("While in Skylark mode", e);
+      }
+
+      enableBuildMode();
+      try {
+        testable.run();
+      } catch (Exception e) {
+        throw new Exception("While in Build mode", e);
+      }
+    }
+  }
+
+  /**
+   * A class that runs all tests in Build mode
+   */
+  protected class BuildTest extends ModalTestCase {
+    public BuildTest() {}
+
+    @Override
+    protected void run(Testable testable) throws Exception {
+      enableBuildMode();
+      testable.run();
+    }
+  }
+
+  /**
+   * A class that runs all tests in Skylark mode
+   */
+  protected class SkylarkTest extends ModalTestCase {
+    public SkylarkTest() {}
+
+    @Override
+    protected void run(Testable testable) throws Exception {
+      enableSkylarkMode();
+      testable.run();
+    }
+  }
 }
diff --git a/src/test/java/com/google/devtools/build/lib/syntax/SkylarkEvaluationTest.java b/src/test/java/com/google/devtools/build/lib/syntax/SkylarkEvaluationTest.java
index 0264681..88f4503 100644
--- a/src/test/java/com/google/devtools/build/lib/syntax/SkylarkEvaluationTest.java
+++ b/src/test/java/com/google/devtools/build/lib/syntax/SkylarkEvaluationTest.java
@@ -15,8 +15,6 @@
 
 import static com.google.common.truth.Truth.assertThat;
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 
 import com.google.common.collect.ImmutableCollection;
@@ -29,8 +27,8 @@
 import com.google.devtools.build.lib.analysis.RuleConfiguredTarget;
 import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
 import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
-import com.google.devtools.build.lib.rules.SkylarkModules;
 import com.google.devtools.build.lib.syntax.ClassObject.SkylarkClassObject;
+import com.google.devtools.build.lib.testutil.TestMode;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -41,13 +39,19 @@
  */
 @RunWith(JUnit4.class)
 public class SkylarkEvaluationTest extends EvaluationTest {
-
-  // Restoring defaults overridden by EvaluationTest
-  @Override
-  public EvaluationContext newEvaluationContext() {
-    return SkylarkModules.newEvaluationContext(getEventHandler());
+  public SkylarkEvaluationTest() throws Exception {
+    setMode(TestMode.SKYLARK);
   }
 
+  /**
+   * Creates an instance of {@code SkylarkTest} in order to run the tests from the base class in a
+   * Skylark context
+   */
+  @Override
+  protected ModalTestCase newTest() {
+    return new SkylarkTest();
+  }
+  
   @SkylarkModule(name = "Mock", doc = "")
   static class Mock {
     @SkylarkCallable(doc = "")
@@ -146,43 +150,34 @@
 
   @Test
   public void testSimpleIf() throws Exception {
-    eval("def foo():",
+    new SkylarkTest().setUp("def foo():",
         "  a = 0",
         "  x = 0",
         "  if x: a = 5",
         "  return a",
-        "a = foo()");
-    assertEquals(0, lookup("a"));
+        "a = foo()").testLookup("a", 0);
   }
 
   @Test
   public void testIfPass() throws Exception {
-    eval("def foo():",
+    new SkylarkTest().setUp("def foo():",
         "  a = 1",
         "  x = True",
         "  if x: pass",
         "  return a",
-        "a = foo()");
-    assertEquals(1, lookup("a"));
+        "a = foo()").testLookup("a", 1);
   }
 
   @Test
   public void testNestedIf() throws Exception {
-    executeNestedIf(0, 0);
-    assertEquals(0, lookup("x"));
-
-    setUp();
-    executeNestedIf(1, 0);
-    assertEquals(3, lookup("x"));
-
-    setUp();
-    executeNestedIf(1, 1);
-    assertEquals(5, lookup("x"));
+    executeNestedIf(0, 0, 0);
+    executeNestedIf(1, 0, 3);
+    executeNestedIf(1, 1, 5);
   }
 
-  private void executeNestedIf(int x, int y) throws Exception {
+  private void executeNestedIf(int x, int y, int expected) throws Exception {
     String fun = String.format("foo%s%s", x, y);
-    eval("def " + fun + "():",
+    new SkylarkTest().setUp("def " + fun + "():",
         "  x = " + x,
         "  y = " + y,
         "  a = 0",
@@ -192,7 +187,7 @@
         "      a = 2",
         "    b = 3",
         "  return a + b",
-        "x = " + fun + "()");
+        "x = " + fun + "()").testLookup("x", expected);
   }
 
   @Test
@@ -202,14 +197,14 @@
   }
 
   private void executeIfElse(String fun, String y, int expected) throws Exception {
-    eval("def " + fun + "():",
+    new SkylarkTest().setUp("def " + fun + "():",
         "  y = '" + y + "'",
         "  x = 5",
         "  if x:",
         "    if y: a = 2",
         "    else: a = 3",
-        "  return a\n");
-    assertEquals(expected, eval(fun + "()"));
+        "  return a",
+        "z = " + fun + "()").testLookup("z", expected);
   }
 
   @Test
@@ -228,7 +223,7 @@
   }
 
   private void execIfElifElse(int x, int y, int v) throws Exception {
-    eval("def foo():",
+    new SkylarkTest().setUp("def foo():",
         "  x = " + x + "",
         "  y = " + y + "",
         "  if x:",
@@ -237,100 +232,89 @@
         "    return 2",
         "  else:",
         "    return 3",
-        "v = foo()");
-    assertEquals(v, lookup("v"));
+        "v = foo()").testLookup("v", v);
   }
 
   @Test
   public void testForOnList() throws Exception {
-    eval("def foo():",
+    new SkylarkTest().setUp("def foo():",
         "  s = ''",
         "  for i in ['hello', ' ', 'world']:",
         "    s = s + i",
         "  return s",
-        "s = foo()");
-    assertEquals("hello world", lookup("s"));
+        "s = foo()").testLookup("s", "hello world");
   }
 
-  @SuppressWarnings("unchecked")
   @Test
   public void testForOnString() throws Exception {
-    eval("def foo():",
+    new SkylarkTest().setUp("def foo():",
         "  s = []",
         "  for i in 'abc':",
         "    s = s + [i]",
         "  return s",
-        "s = foo()");
-    assertThat((Iterable<Object>) lookup("s")).containsExactly("a", "b", "c").inOrder();
+        "s = foo()").testExactOrder("s", "a", "b", "c");
   }
 
   @Test
   public void testForAssignmentList() throws Exception {
-    eval("def foo():",
+    new SkylarkTest().setUp("def foo():",
         "  d = ['a', 'b', 'c']",
         "  s = ''",
         "  for i in d:",
         "    s = s + i",
-        "    d = ['d', 'e', 'f']",  // check that we use the old list
+        "    d = ['d', 'e', 'f']", // check that we use the old list
         "  return s",
-        "s = foo()");
-    assertEquals("abc", lookup("s"));
+        "s = foo()").testLookup("s", "abc");
   }
 
   @Test
   public void testForAssignmentDict() throws Exception {
-    eval("def func():",
+    new SkylarkTest().setUp("def func():",
         "  d = {'a' : 1, 'b' : 2, 'c' : 3}",
         "  s = ''",
         "  for i in d:",
         "    s = s + i",
         "    d = {'d' : 1, 'e' : 2, 'f' : 3}",
         "  return s",
-        "s = func()");
-    assertEquals("abc", lookup("s"));
+        "s = func()").testLookup("s", "abc");
   }
 
   @Test
   public void testForNotIterable() throws Exception {
-    update("mock", new Mock());
-    checkEvalError("type 'int' is not iterable",
-        "def func():",
-        "  for i in mock.value_of('1'): a = i",
-        "func()");
-
+    new SkylarkTest()
+        .update("mock", new Mock())
+        .testIfExactError("type 'int' is not iterable", "def func():",
+            "  for i in mock.value_of('1'): a = i", "func()\n");
   }
 
   @Test
   public void testForOnDictionary() throws Exception {
-    eval("def foo():",
+    new SkylarkTest().setUp("def foo():",
         "  d = {1: 'a', 2: 'b', 3: 'c'}",
         "  s = ''",
         "  for i in d: s = s + d[i]",
         "  return s",
-        "s = foo()");
-    assertEquals("abc", lookup("s"));
+        "s = foo()").testLookup("s", "abc");
   }
 
   @Test
   public void testForLoopReuseVariable() throws Exception {
-    eval("def foo():",
+    new SkylarkTest().setUp("def foo():",
         "  s = ''",
         "  for i in ['a', 'b']:",
         "    for i in ['c', 'd']: s = s + i",
         "  return s",
-        "s = foo()");
-    assertEquals("cdcd", lookup("s"));
+        "s = foo()").testLookup("s", "cdcd");
   }
 
   @Test
   public void testForLoopMultipleVariables() throws Exception {
-    eval("def foo():",
+    new SkylarkTest().setUp("def foo():",
         "  s = ''",
         "  for [i, j] in [[1, 2], [3, 4]]:",
         "    s = s + str(i) + str(j) + '.'",
         "  return s",
-        "s = foo()");
-    assertEquals("12.34.", lookup("s"));
+        "s = foo()").testLookup("s", "12.34.");
   }
 
   @Test
@@ -457,15 +441,12 @@
         "   " + statement + "", 
         "y = foo2()");
   }
-  
+
   @Test
   public void testNoneAssignment() throws Exception {
-    eval("def foo(x=None):",
-        "  x = 1",
-        "  x = None",
-        "  return 2",
-        "s = foo()");
-    assertEquals(2, lookup("s"));
+    new SkylarkTest()
+        .setUp("def foo(x=None):", "  x = 1", "  x = None", "  return 2", "s = foo()")
+        .testLookup("s", 2);
   }
 
   @Test
@@ -481,98 +462,111 @@
 
   @Test
   public void testJavaCalls() throws Exception {
-    update("mock", new Mock());
-    eval("b = mock.is_empty('a')");
-    assertEquals(Boolean.FALSE, lookup("b"));
+    new SkylarkTest()
+        .update("mock", new Mock())
+        .setUp("b = mock.is_empty('a')")
+        .testLookup("b", Boolean.FALSE);
   }
 
   @Test
   public void testJavaCallsOnSubClass() throws Exception {
-    update("mock", new MockSubClass());
-    eval("b = mock.is_empty('a')");
-    assertEquals(Boolean.FALSE, lookup("b"));
+    new SkylarkTest()
+        .update("mock", new MockSubClass())
+        .setUp("b = mock.is_empty('a')")
+        .testLookup("b", Boolean.FALSE);
   }
 
   @Test
   public void testJavaCallsOnInterface() throws Exception {
-    update("mock", new MockSubClass());
-    eval("b = mock.is_empty_interface('a')");
-    assertEquals(Boolean.FALSE, lookup("b"));
+    new SkylarkTest()
+        .update("mock", new MockSubClass())
+        .setUp("b = mock.is_empty_interface('a')")
+        .testLookup("b", Boolean.FALSE);
   }
 
   @Test
   public void testJavaCallsNotSkylarkCallable() throws Exception {
-    update("mock", new Mock());
-    checkEvalError("No matching method found for value() in Mock", "mock.value()");
+    new SkylarkTest()
+        .update("mock", new Mock())
+        .testIfExactError("No matching method found for value() in Mock", "mock.value()");
   }
 
   @Test
   public void testJavaCallsNoMethod() throws Exception {
-    checkEvalError("No matching method found for bad() in int", "s = 3.bad()");
+    new SkylarkTest().testIfExactError("No matching method found for bad() in int", "s = 3.bad()");
   }
 
   @Test
   public void testJavaCallsNoMethodErrorMsg() throws Exception {
-    checkEvalError("No matching method found for bad(string, string, string) in int",
+    new SkylarkTest().testIfExactError(
+        "No matching method found for bad(string, string, string) in int",
         "s = 3.bad('a', 'b', 'c')");
   }
 
   @Test
   public void testJavaCallsMultipleMethod() throws Exception {
-    update("mock", new MockMultipleMethodClass());
-    checkEvalError("Multiple matching methods for method(string) in MockMultipleMethodClass",
-        "s = mock.method('string')");
+    new SkylarkTest()
+        .update("mock", new MockMultipleMethodClass())
+        .testIfExactError(
+            "Multiple matching methods for method(string) in MockMultipleMethodClass",
+            "s = mock.method('string')");
   }
 
   @Test
   public void testJavaCallWithKwargs() throws Exception {
-    checkEvalError("Keyword arguments are not allowed when calling a java method"
+    new SkylarkTest().testIfExactError(
+        "Keyword arguments are not allowed when calling a java method"
         + "\nwhile calling method 'compare_to' on object 3 of type int",
         "comp = 3.compare_to(x = 4)");
   }
 
   @Test
   public void testNoJavaCallsWithoutSkylark() throws Exception {
-    checkEvalError("No matching method found for to_string() in int", "s = 3.to_string()\n");
+    new SkylarkTest().testIfExactError(
+        "No matching method found for to_string() in int", "s = 3.to_string()");
   }
 
   @Test
   public void testNoJavaCallsIfClassNotAnnotated() throws Exception {
-    update("mock", new MockSubClass());
-    checkEvalError(
-        "No matching method found for is_empty_class_not_annotated(string) in MockSubClass",
-        "b = mock.is_empty_class_not_annotated('a')");
+    new SkylarkTest()
+        .update("mock", new MockSubClass())
+        .testIfExactError(
+            "No matching method found for is_empty_class_not_annotated(string) in MockSubClass",
+            "b = mock.is_empty_class_not_annotated('a')");
   }
 
   @Test
   public void testStructAccess() throws Exception {
-    update("mock", new Mock());
-    eval("v = mock.struct_field");
-    assertEquals("a", lookup("v"));
+    new SkylarkTest()
+        .update("mock", new Mock())
+        .setUp("v = mock.struct_field")
+        .testLookup("v", "a");
   }
 
   @Test
   public void testStructAccessAsFuncall() throws Exception {
-    update("mock", new Mock());
-    checkEvalError("No matching method found for struct_field() in Mock",
-        "v = mock.struct_field()");
+    new SkylarkTest()
+        .update("mock", new Mock())
+        .testIfExactError(
+            "No matching method found for struct_field() in Mock", "v = mock.struct_field()");
   }
 
   @Test
   public void testStructAccessOfMethod() throws Exception {
-    update("mock", new Mock());
-    checkEvalError("Object of type 'Mock' has no field \"function\"",
-        "v = mock.function");
+    new SkylarkTest()
+        .update("mock", new Mock())
+        .testIfExactError("Object of type 'Mock' has no field \"function\"", "v = mock.function");
   }
 
   @Test
   public void testConditionalStructConcatenation() throws Exception {
+    // TODO(fwe): cannot be handled by current testing suite
     eval("def func():",
         "  x = struct(a = 1, b = 2)",
         "  if True:",
         "    x += struct(c = 1, d = 2)",
         "  return x",
-        "x = func()\n");
+        "x = func()");
     SkylarkClassObject x = (SkylarkClassObject) lookup("x");
     assertEquals(1, x.getValue("a"));
     assertEquals(2, x.getValue("b"));
@@ -582,96 +576,103 @@
 
   @Test
   public void testJavaFunctionReturnsMutableObject() throws Exception {
-    update("mock", new Mock());
-    checkEvalError("Method 'return_mutable' returns a mutable object (type of Mock)",
-        "mock.return_mutable()");
+    new SkylarkTest()
+        .update("mock", new Mock())
+        .testIfExactError(
+            "Method 'return_mutable' returns a mutable object (type of Mock)",
+            "mock.return_mutable()");
   }
 
   @Test
   public void testJavaFunctionReturnsNullFails() throws Exception {
-    update("mock", new Mock());
-    checkEvalError("Method invocation returned None,"
-        + " please contact Skylark developers: nullfunc_failing(\"abc\", 1)",
-        "mock.nullfunc_failing('abc', 1)");
+    new SkylarkTest()
+        .update("mock", new Mock())
+        .testIfExactError(
+            "Method invocation returned None,"
+            + " please contact Skylark developers: nullfunc_failing(\"abc\", 1)",
+            "mock.nullfunc_failing('abc', 1)");
   }
 
   @Test
   public void testClassObjectAccess() throws Exception {
-    update("mock", new MockClassObject());
-    eval("v = mock.field");
-    assertEquals("a", lookup("v"));
+    new SkylarkTest()
+        .update("mock", new MockClassObject())
+        .setUp("v = mock.field")
+        .testLookup("v", "a");
   }
 
   @Test
   public void testInSet() throws Exception {
-    assertEquals(Boolean.TRUE, eval("'b' in set(['a', 'b'])"));
-    assertEquals(Boolean.FALSE, eval("'c' in set(['a', 'b'])"));
-    assertEquals(Boolean.FALSE, eval("1 in set(['a', 'b'])"));
+    new SkylarkTest().testStatement("'b' in set(['a', 'b'])", Boolean.TRUE)
+        .testStatement("'c' in set(['a', 'b'])", Boolean.FALSE)
+        .testStatement("1 in set(['a', 'b'])", Boolean.FALSE);
   }
 
   @Test
   public void testClassObjectCannotAccessNestedSet() throws Exception {
-    update("mock", new MockClassObject());
-    checkEvalError("Type is not allowed in Skylark: EmptyNestedSet", "v = mock.nset");
+    new SkylarkTest()
+        .update("mock", new MockClassObject())
+        .testIfExactError("Type is not allowed in Skylark: EmptyNestedSet", "v = mock.nset");
   }
 
   @Test
   public void testJavaFunctionReturnsNone() throws Exception {
-    update("mock", new Mock());
-    eval("v = mock.nullfunc_working()");
-    assertSame(Environment.NONE, lookup("v"));
+    new SkylarkTest()
+        .update("mock", new Mock())
+        .setUp("v = mock.nullfunc_working()")
+        .testLookup("v", Environment.NONE);
   }
 
   @Test
   public void testVoidJavaFunctionReturnsNone() throws Exception {
-    update("mock", new Mock());
-    eval("v = mock.voidfunc()");
-    assertSame(Environment.NONE, lookup("v"));
+    new SkylarkTest()
+        .update("mock", new Mock())
+        .setUp("v = mock.voidfunc()")
+        .testLookup("v", Environment.NONE);
   }
 
   @Test
   public void testAugmentedAssignment() throws Exception {
-    eval("def f1(x):",
+    new SkylarkTest().setUp("def f1(x):",
         "  x += 1",
         "  return x",
         "",
-        "foo = f1(41)\n");
-    assertEquals(42, lookup("foo"));
+        "foo = f1(41)").testLookup("foo", 42);
   }
 
   @Test
   public void testStaticDirectJavaCall() throws Exception {
-    update("Mock", Mock.class);
-    eval("val = Mock.value_of('8')");
-    assertEquals(8, lookup("val"));
+    new SkylarkTest().update("Mock", Mock.class).setUp("val = Mock.value_of('8')")
+        .testLookup("val", 8);
   }
 
   @Test
   public void testStaticDirectJavaCallMethodIsNonStatic() throws Exception {
-    update("Mock", Mock.class);
-    checkEvalError("Method 'is_empty' is not static", "val = Mock.is_empty('a')");
+    new SkylarkTest().update("Mock", Mock.class).testIfExactError("Method 'is_empty' is not static",
+        "val = Mock.is_empty('a')");
   }
 
   @Test
   public void testDictComprehensions_IterationOrder() throws Exception {
-    eval("def foo():",
+    new SkylarkTest().setUp("def foo():",
         "  d = {x : x for x in ['c', 'a', 'b']}",
         "  s = ''",
         "  for a in d:",
         "    s += a",
         "  return s",
-        "s = foo()");
-    assertEquals("abc", lookup("s"));
+        "s = foo()").testLookup("s", "abc");
   }
 
   @Test
   public void testStructCreation() throws Exception {
+    // TODO(fwe): cannot be handled by current testing suite
     eval("x = struct(a = 1, b = 2)");
     assertThat(lookup("x")).isInstanceOf(ClassObject.class);
   }
 
   @Test
   public void testStructFields() throws Exception {
+    // TODO(fwe): cannot be handled by current testing suite
     eval("x = struct(a = 1, b = 2)");
     ClassObject x = (ClassObject) lookup("x");
     assertEquals(1, x.getValue("a"));
@@ -680,35 +681,33 @@
 
   @Test
   public void testStructAccessingFieldsFromSkylark() throws Exception {
-    eval("x = struct(a = 1, b = 2)",
-        "x1 = x.a",
-        "x2 = x.b\n");
-    assertEquals(1, lookup("x1"));
-    assertEquals(2, lookup("x2"));
+    new SkylarkTest()
+        .setUp("x = struct(a = 1, b = 2)", "x1 = x.a", "x2 = x.b")
+        .testLookup("x1", 1)
+        .testLookup("x2", 2);
   }
 
   @Test
   public void testStructAccessingUnknownField() throws Exception {
-    checkEvalError("Object of type 'struct' has no field \"c\"",
-        "x = struct(a = 1, b = 2)",
-        "y = x.c\n");
+    new SkylarkTest().testIfExactError(
+        "Object of type 'struct' has no field \"c\"", "x = struct(a = 1, b = 2)", "y = x.c");
   }
 
   @Test
   public void testStructAccessingFieldsWithArgs() throws Exception {
-    checkEvalError("No matching method found for a(int) in struct",
-        "x = struct(a = 1, b = 2)",
-        "x1 = x.a(1)\n");
+    new SkylarkTest().testIfExactError(
+        "No matching method found for a(int) in struct", "x = struct(a = 1, b = 2)", "x1 = x.a(1)");
   }
 
   @Test
   public void testStructPosArgs() throws Exception {
-    checkEvalError("struct(**kwargs) does not accept positional arguments, but got 1",
-        "x = struct(1, b = 2)\n");
+    new SkylarkTest().testIfExactError(
+        "struct(**kwargs) does not accept positional arguments, but got 1", "x = struct(1, b = 2)");
   }
 
   @Test
   public void testStructConcatenationFieldNames() throws Exception {
+    // TODO(fwe): cannot be handled by current testing suite
     eval("x = struct(a = 1, b = 2)",
         "y = struct(c = 1, d = 2)",
         "z = x + y\n");
@@ -718,6 +717,7 @@
 
   @Test
   public void testStructConcatenationFieldValues() throws Exception {
+    // TODO(fwe): cannot be handled by current testing suite
     eval("x = struct(a = 1, b = 2)",
         "y = struct(c = 1, d = 2)",
         "z = x + y\n");
@@ -730,49 +730,46 @@
 
   @Test
   public void testStructConcatenationCommonFields() throws Exception {
-    checkEvalError("Cannot concat structs with common field(s): a",
-        "x = struct(a = 1, b = 2)",
-        "y = struct(c = 1, a = 2)",
-        "z = x + y\n");
+    new SkylarkTest().testIfExactError("Cannot concat structs with common field(s): a",
+        "x = struct(a = 1, b = 2)", "y = struct(c = 1, a = 2)", "z = x + y\n");
   }
 
   @Test
   public void testDotExpressionOnNonStructObject() throws Exception {
-    checkEvalError("Object of type 'string' has no field \"field\"", "x = 'a'.field");
+    new SkylarkTest().testIfExactError("Object of type 'string' has no field \"field\"",
+        "x = 'a'.field");
   }
 
   @Test
   public void testPlusEqualsOnDict() throws Exception {
-    eval("def func():",
+    new SkylarkTest().setUp("def func():",
         "  d = {'a' : 1}",
         "  d += {'b' : 2}",
         "  return d",
-        "d = func()");
-    assertEquals(ImmutableMap.of("a", 1, "b", 2), lookup("d"));
+        "d = func()")
+        .testLookup("d", ImmutableMap.of("a", 1, "b", 2));
   }
 
   @Test
   public void testDictAssignmentAsLValue() throws Exception {
-    eval("def func():",
+    new SkylarkTest().setUp("def func():",
         "  d = {'a' : 1}",
         "  d['b'] = 2",
         "  return d",
-        "d = func()");
-    assertEquals(ImmutableMap.of("a", 1, "b", 2), lookup("d"));
+        "d = func()").testLookup("d", ImmutableMap.of("a", 1, "b", 2));
   }
 
   @Test
   public void testDictAssignmentAsLValueNoSideEffects() throws Exception {
-    eval("def func(d):",
+    new SkylarkTest().setUp("def func(d):",
         "  d['b'] = 2",
         "d = {'a' : 1}",
-        "func(d)");
-    assertEquals(ImmutableMap.of("a", 1), lookup("d"));
+        "func(d)").testLookup("d", ImmutableMap.of("a", 1));
   }
 
   @Test
   public void testListIndexAsLValueAsLValue() throws Exception {
-    checkEvalError("unsupported operand type(s) for +: 'list' and 'dict'",
+    new SkylarkTest().testIfExactError("unsupported operand type(s) for +: 'list' and 'dict'",
         "def id(l):",
         "  return l",
         "def func():",
@@ -784,32 +781,30 @@
 
   @Test
   public void testTopLevelDict() throws Exception {
-    eval("if 1:",
+    new SkylarkTest().setUp("if 1:",
       "  v = 'a'",
       "else:",
-      "  v = 'b'");
-    assertEquals("a", lookup("v"));
+      "  v = 'b'").testLookup("v", "a");
   }
 
   @Test
   public void testUserFunctionKeywordArgs() throws Exception {
-    eval("def foo(a, b, c):",
-      "  return a + b + c",
-      "s = foo(1, c=2, b=3)");
-    assertEquals(6, lookup("s"));
+    new SkylarkTest().setUp("def foo(a, b, c):", 
+        "  return a + b + c", "s = foo(1, c=2, b=3)")
+        .testLookup("s", 6);
   }
 
   @Test
   public void testFunctionCallOrdering() throws Exception {
-    eval("def func(): return foo() * 2",
+    new SkylarkTest().setUp("def func(): return foo() * 2",
          "def foo(): return 2",
-         "x = func()");
-    assertThat(lookup("x")).isEqualTo(4);
+         "x = func()")
+         .testLookup("x", 4);
   }
 
   @Test
   public void testFunctionCallBadOrdering() throws Exception {
-    checkEvalError("name 'foo' is not defined",
+    new SkylarkTest().testIfExactError("name 'foo' is not defined",
          "def func(): return foo() * 2",
          "x = func()",
          "def foo(): return 2");
@@ -817,118 +812,111 @@
 
   @Test
   public void testNoneTrueFalseInSkylark() throws Exception {
-    eval("a = None",
+    new SkylarkTest().setUp("a = None",
       "b = True",
-      "c = False");
-    assertSame(Environment.NONE, lookup("a"));
-    assertTrue((Boolean) lookup("b"));
-    assertFalse((Boolean) lookup("c"));
+      "c = False")
+      .testLookup("a", Environment.NONE)
+      .testLookup("b", Boolean.TRUE)
+      .testLookup("c", Boolean.FALSE);
   }
 
   @Test
   public void testHasattr() throws Exception {
-    eval("s = struct(a=1)",
+    new SkylarkTest().setUp("s = struct(a=1)",
       "x = hasattr(s, 'a')",
-      "y = hasattr(s, 'b')\n");
-    assertTrue((Boolean) lookup("x"));
-    assertFalse((Boolean) lookup("y"));
+      "y = hasattr(s, 'b')\n")
+      .testLookup("x", Boolean.TRUE)
+      .testLookup("y", Boolean.FALSE);
   }
 
   @Test
   public void testHasattrMethods() throws Exception {
-    update("mock", new Mock());
-    eval("a = hasattr(mock, 'struct_field')",
-        "b = hasattr(mock, 'function')",
-        "c = hasattr(mock, 'is_empty')",
-        "d = hasattr('str', 'replace')",
-        "e = hasattr(mock, 'other')\n");
-    assertTrue((Boolean) lookup("a"));
-    assertTrue((Boolean) lookup("b"));
-    assertTrue((Boolean) lookup("c"));
-    assertTrue((Boolean) lookup("d"));
-    assertFalse((Boolean) lookup("e"));
+    new SkylarkTest()
+        .update("mock", new Mock())
+        .setUp("a = hasattr(mock, 'struct_field')", "b = hasattr(mock, 'function')",
+            "c = hasattr(mock, 'is_empty')", "d = hasattr('str', 'replace')",
+            "e = hasattr(mock, 'other')\n")
+        .testLookup("a", Boolean.TRUE)
+        .testLookup("b", Boolean.TRUE)
+        .testLookup("c", Boolean.TRUE)
+        .testLookup("d", Boolean.TRUE)
+        .testLookup("e", Boolean.FALSE);
   }
 
   @Test
   public void testGetattr() throws Exception {
-    eval("s = struct(a='val')",
-      "x = getattr(s, 'a')",
-      "y = getattr(s, 'b', 'def')",
-      "z = getattr(s, 'b', default = 'def')",
-      "w = getattr(s, 'a', default='ignored')");
-    assertEquals("val", lookup("x"));
-    assertEquals("def", lookup("y"));
-    assertEquals("def", lookup("z"));
-    assertEquals("val", lookup("w"));
+    new SkylarkTest()
+        .setUp("s = struct(a='val')", "x = getattr(s, 'a')", "y = getattr(s, 'b', 'def')",
+            "z = getattr(s, 'b', default = 'def')", "w = getattr(s, 'a', default='ignored')")
+        .testLookup("x", "val")
+        .testLookup("y", "def")
+        .testLookup("z", "def")
+        .testLookup("w", "val");
   }
 
   @Test
   public void testGetattrNoAttr() throws Exception {
-    checkEvalError("Object of type 'struct' has no field \"b\"",
-        "s = struct(a='val')",
-        "getattr(s, 'b')");
+    new SkylarkTest().testIfExactError("Object of type 'struct' has no field \"b\"",
+        "s = struct(a='val')", "getattr(s, 'b')");
   }
 
-  @SuppressWarnings("unchecked")
   @Test
   public void testListAnTupleConcatenationDoesNotWorkInSkylark() throws Exception {
-    checkEvalError("cannot concatenate lists and tuples", "[1, 2] + (3, 4)");
+    new SkylarkTest().testIfExactError("cannot concatenate lists and tuples", "[1, 2] + (3, 4)");
   }
 
   @Test
   public void testCannotCreateMixedListInSkylark() throws Exception {
-    update("mock", new Mock());
-    checkEvalError(
+    new SkylarkTest().update("mock", new Mock()).testIfExactError(
         "Incompatible types in list: found a int but the previous elements were strings",
         "[mock.string(), 1, 2]");
   }
 
   @Test
   public void testCannotConcatListInSkylarkWithDifferentGenericTypes() throws Exception {
-    update("mock", new Mock());
-    checkEvalError("cannot concatenate list of string with list of int",
-        "mock.string_list() + [1, 2]");
+    new SkylarkTest().update("mock", new Mock()).testIfExactError(
+        "cannot concatenate list of string with list of int", "mock.string_list() + [1, 2]");
   }
 
-  @SuppressWarnings("unchecked")
   @Test
   public void testConcatEmptyListWithNonEmptyWorks() throws Exception {
-    eval("l = [] + ['a', 'b']");
-    assertThat((Iterable<Object>) lookup("l")).containsExactly("a", "b").inOrder();
+    new SkylarkTest().testExactOrder("[] + ['a', 'b']", "a", "b");
   }
 
   @Test
   public void testFormatStringWithTuple() throws Exception {
-    eval("v = '%s%s' % ('a', 1)");
-    assertEquals("a1", lookup("v"));
+    new SkylarkTest().setUp("v = '%s%s' % ('a', 1)").testLookup("v", "a1");
   }
 
   @Test
   public void testSingletonTuple() throws Exception {
-    eval("v = (1,)");
-    assertEquals("(1,)", lookup("v").toString());
+    new SkylarkTest().testExactOrder("(1,)", 1);
   }
 
-  @SuppressWarnings("unchecked")
   @Test
   public void testDirFindsClassObjectFields() throws Exception {
-    update("mock", new MockClassObject());
-    eval("v = dir(mock)");
-    assertThat((Iterable<String>) lookup("v")).containsExactly("field", "nset").inOrder();
+    new SkylarkTest().update("mock", new MockClassObject()).setUp()
+        .testExactOrder("dir(mock)", "field", "nset");
   }
 
-  @SuppressWarnings("unchecked")
   @Test
   public void testDirFindsJavaObjectStructFieldsAndMethods() throws Exception {
-    update("mock", new Mock());
-    eval("v = dir(mock)");
-    assertThat((Iterable<String>) lookup("v")).containsExactly("function", "is_empty",
-        "nullfunc_failing", "nullfunc_working", "return_mutable", "string", "string_list",
-        "struct_field", "value_of", "voidfunc").inOrder();
+    new SkylarkTest().update("mock", new Mock()).testExactOrder("dir(mock)",
+        "function",
+        "is_empty",
+        "nullfunc_failing",
+        "nullfunc_working",
+        "return_mutable",
+        "string",
+        "string_list",
+        "struct_field",
+        "value_of",
+        "voidfunc");
   }
 
   @Test
   public void testPrint() throws Exception {
+    // TODO(fwe): cannot be handled by current testing suite
     setFailFast(false);
     eval("print('hello')");
     assertContainsEvent("hello");
@@ -940,7 +928,7 @@
 
   @Test
   public void testPrintBadKwargs() throws Exception {
-    checkEvalError(
+    new SkylarkTest().testIfExactError(
         "unexpected keywords 'end', 'other' in call to print(*args, sep: string = \" \")",
         "print(end='x', other='y')");
   }
@@ -960,6 +948,10 @@
   @Override
   @Test
   public void testConcatLists() throws Exception {
+    new SkylarkTest().testExactOrder("[1,2] + [3,4]", 1, 2, 3, 4).testExactOrder("(1,2)", 1, 2)
+        .testExactOrder("(1,2) + (3,4)", 1, 2, 3, 4);
+
+    // TODO(fwe): cannot be handled by current testing suite
     // list
     Object x = eval("[1,2] + [3,4]");
     assertThat((Iterable<Object>) x).containsExactly(1, 2, 3, 4).inOrder();
@@ -981,56 +973,56 @@
   @Override
   @Test
   public void testInFail() throws Exception {
-    checkEvalError("in operator only works on strings if the left operand is also a string",
-        "1 in '123'");
-    checkEvalError("in operator only works on lists, tuples, sets, dicts and strings",
-        "'a' in 1");
+    new SkylarkTest().testIfExactError(
+        "in operator only works on strings if the left operand is also a string", "1 in '123'");
+    new SkylarkTest().testIfExactError(
+        "in operator only works on lists, tuples, sets, dicts and strings", "'a' in 1");
   }
 
   @Override
   @Test
   public void testCompareStringInt() throws Exception {
-    checkEvalError("Cannot compare string with int", "'a' >= 1");
+    new SkylarkTest().testIfExactError("Cannot compare string with int", "'a' >= 1");
   }
 
   @Override
   @Test
   public void testListComprehensionsMultipleVariablesFail() throws Exception {
-    checkEvalError("lvalue has length 3, but rvalue has has length 2",
+    new SkylarkTest().testIfExactError("lvalue has length 3, but rvalue has has length 2",
         "def foo (): return [x + y for x, y, z in [(1, 2), (3, 4)]]",
         "foo()");
 
-    checkEvalError("type 'int' is not a collection",
+    new SkylarkTest().testIfExactError("type 'int' is not a collection",
         "def bar (): return [x + y for x, y in (1, 2)]",
         "bar()");
 
-    checkEvalError("lvalue has length 3, but rvalue has has length 2",
+    new SkylarkTest().testIfExactError("lvalue has length 3, but rvalue has has length 2",
         "[x + y for x, y, z in [(1, 2), (3, 4)]]");
 
     // can't reuse the same local variable twice(!)
-    checkEvalError("ERROR 1:1: Variable x is read only", "[x + y for x, y in (1, 2)]");
+    new SkylarkTest().testIfExactError("ERROR 2:1: Variable x is read only",
+        "[x + y for x, y in (1, 2)]", "[x + y for x, y in (1, 2)]");
 
-    checkEvalError("type 'int' is not a collection", "[x2 + y2 for x2, y2 in (1, 2)]");
+    new SkylarkTest().testIfExactError("type 'int' is not a collection",
+        "[x2 + y2 for x2, y2 in (1, 2)]");
   }
 
   @Override
   @Test
   public void testNotCallInt() throws Exception {
-    eval("sum = 123456");
-    assertEquals(123456, lookup("sum"));
-    checkEvalError("'int' object is not callable", "sum(1, 2, 3, 4, 5, 6)");
-    assertEquals(123456, eval("sum"));
+    new SkylarkTest().setUp("sum = 123456").testLookup("sum", 123456)
+        .testIfExactError("'int' object is not callable", "sum(1, 2, 3, 4, 5, 6)")
+        .testStatement("sum", 123456);
   }
 
   @Test
   public void testConditionalExpressionAtToplevel() throws Exception {
-    eval("x = 1 if 2 else 3");
-    assertEquals(1, lookup("x"));
+    new SkylarkTest().setUp("x = 1 if 2 else 3").testLookup("x", 1);
   }
 
   @Test
   public void testConditionalExpressionInFunction() throws Exception {
-    eval("def foo(a, b, c): return a+b if c else a-b\n");
-    assertEquals(18, eval("foo(23, 5, 0)"));
+    new SkylarkTest().setUp("def foo(a, b, c): return a+b if c else a-b\n").testStatement(
+        "foo(23, 5, 0)", 18);
   }
 }
diff --git a/src/test/java/com/google/devtools/build/lib/testutil/TestMode.java b/src/test/java/com/google/devtools/build/lib/testutil/TestMode.java
new file mode 100644
index 0000000..5ec7ac2
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/testutil/TestMode.java
@@ -0,0 +1,44 @@
+// Copyright 2015 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.google.devtools.build.lib.testutil;
+
+import com.google.devtools.build.lib.events.EventHandler;
+import com.google.devtools.build.lib.rules.SkylarkModules;
+import com.google.devtools.build.lib.syntax.Environment;
+import com.google.devtools.build.lib.syntax.EvaluationContext;
+
+/**
+ * Describes a particular testing mode by determining how the
+ * appropriate {@code EvaluationContext} has to be created
+ */
+public abstract class TestMode {
+  public static final TestMode BUILD = new TestMode() {
+    @Override
+    public EvaluationContext createContext(EventHandler eventHandler, Environment environment)
+        throws Exception {
+      return EvaluationContext.newBuildContext(eventHandler, environment);
+    }
+  };
+
+  public static final TestMode SKYLARK = new TestMode() {
+    @Override
+    public EvaluationContext createContext(EventHandler eventHandler, Environment environment)
+        throws Exception {
+      return SkylarkModules.newEvaluationContext(eventHandler);
+    }
+  };
+
+  public abstract EvaluationContext createContext(
+      EventHandler eventHandler, Environment environment) throws Exception;
+}
\ No newline at end of file