// Copyright © 2011-2013, Esko Luontola <www.orfjackal.net> // This software is released under the Apache License 2.0. // The license text is at http://www.apache.org/licenses/LICENSE-2.0 package fi.jumi.simpleunit; import fi.jumi.actors.ActorRef; import fi.jumi.api.RunVia; import fi.jumi.api.drivers.*; import fi.jumi.core.api.*; import fi.jumi.core.drivers.DriverRunner; import fi.jumi.core.results.*; import fi.jumi.core.runs.*; import fi.jumi.core.stdout.OutputCapturer; import fi.jumi.core.testbench.TestBench; import fi.jumi.core.util.SpyListener; import org.junit.Test; import java.util.concurrent.Executor; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.*; public class SimpleUnitTest { private static final RunId RUN_1 = new RunId(1); private final TestBench testBench = new TestBench(); // TODO: think of a high-level API to write tests against, so that it hides Jumi's low-level event protocol @Test public void the_test_class_is_named_after_its_simple_name() { Class<OnePassingTest> testClass = OnePassingTest.class; SuiteEventDemuxer results = testBench.run(testClass); assertThat(results.getTestName(TestFile.fromClass(testClass), TestId.ROOT), is("OnePassingTest")); } @Test public void the_tests_are_methods_whose_name_starts_with_test() throws InterruptedException { Class<OnePassingTest> testClass = OnePassingTest.class; SuiteEventDemuxer results = testBench.run(testClass); assertThat(results.getTestName(TestFile.fromClass(testClass), TestId.of(0)), is("testPassing")); } @Test public void reports_test_execution() throws InterruptedException { SpyListener<RunVisitor> spy = new SpyListener<>(RunVisitor.class); RunVisitor listener = spy.getListener(); Class<OnePassingTest> testClass = OnePassingTest.class; TestFile testFile = TestFile.fromClass(testClass); listener.onRunStarted(RUN_1, testFile); listener.onTestStarted(RUN_1, testFile, TestId.ROOT); listener.onTestStarted(RUN_1, testFile, TestId.of(0)); listener.onTestFinished(RUN_1, testFile, TestId.of(0)); listener.onTestFinished(RUN_1, testFile, TestId.ROOT); listener.onRunFinished(RUN_1, testFile); spy.replay(); SuiteEventDemuxer results = testBench.run(testClass); results.visitAllRuns(listener); spy.verify(); } @Test public void reports_test_failure() throws InterruptedException { SpyListener<RunVisitor> spy = new SpyListener<>(RunVisitor.class); RunVisitor listener = spy.getListener(); Class<OneFailingTest> testClass = OneFailingTest.class; TestFile testFile = TestFile.fromClass(testClass); listener.onRunStarted(RUN_1, testFile); listener.onTestStarted(RUN_1, testFile, TestId.ROOT); listener.onTestStarted(RUN_1, testFile, TestId.of(0)); listener.onFailure(RUN_1, testFile, TestId.of(0), StackTrace.from(new AssertionError("dummy failure"))); listener.onTestFinished(RUN_1, testFile, TestId.of(0)); listener.onTestFinished(RUN_1, testFile, TestId.ROOT); listener.onRunFinished(RUN_1, testFile); spy.replay(); SuiteEventDemuxer results = testBench.run(testClass); results.visitAllRuns(listener); spy.verify(); } @Test public void reports_failures_in_constructor() throws InterruptedException { SpyListener<RunVisitor> spy = new SpyListener<>(RunVisitor.class); RunVisitor listener = spy.getListener(); Class<FailureInConstructorTest> testClass = FailureInConstructorTest.class; TestFile testFile = TestFile.fromClass(testClass); listener.onRunStarted(RUN_1, testFile); listener.onTestStarted(RUN_1, testFile, TestId.ROOT); listener.onFailure(RUN_1, testFile, TestId.ROOT, StackTrace.from(new RuntimeException("dummy exception"))); listener.onTestFinished(RUN_1, testFile, TestId.ROOT); listener.onRunFinished(RUN_1, testFile); spy.replay(); SuiteEventDemuxer results = testBench.run(testClass); results.visitAllRuns(listener); spy.verify(); assertThat("should find the test method even though it fails to run it", results.getTestName(TestFile.fromClass(testClass), TestId.of(0)), is("testNotExecuted")); } @Test public void reports_illegal_test_method_signatures() throws InterruptedException { SpyListener<RunVisitor> spy = new SpyListener<>(RunVisitor.class); RunVisitor listener = spy.getListener(); Class<IllegalTestMethodSignatureTest> testClass = IllegalTestMethodSignatureTest.class; TestFile testFile = TestFile.fromClass(testClass); listener.onRunStarted(RUN_1, testFile); listener.onTestStarted(RUN_1, testFile, TestId.ROOT); listener.onTestStarted(RUN_1, testFile, TestId.of(0)); listener.onFailure(RUN_1, testFile, TestId.of(0), StackTrace.from(new IllegalArgumentException("wrong number of arguments"))); listener.onTestFinished(RUN_1, testFile, TestId.of(0)); listener.onTestFinished(RUN_1, testFile, TestId.ROOT); listener.onRunFinished(RUN_1, testFile); spy.replay(); SuiteEventDemuxer results = testBench.run(testClass); results.visitAllRuns(listener); spy.verify(); assertThat("should find the test method even though it fails to run it", results.getTestName(testFile, TestId.of(0)), is("testMethodWithParameters")); } @Test public void reports_an_error_if_the_test_class_contains_no_test_methods() throws InterruptedException { SpyListener<RunVisitor> spy = new SpyListener<>(RunVisitor.class); RunVisitor listener = spy.getListener(); Class<NoTestMethodsTest> testClass = NoTestMethodsTest.class; TestFile testFile = TestFile.fromClass(testClass); listener.onRunStarted(RUN_1, testFile); listener.onTestStarted(RUN_1, testFile, TestId.ROOT); listener.onFailure(RUN_1, testFile, TestId.ROOT, StackTrace.from(new IllegalArgumentException("No test methods in class fi.jumi.simpleunit.SimpleUnitTest$NoTestMethodsTest"))); listener.onTestFinished(RUN_1, testFile, TestId.ROOT); listener.onRunFinished(RUN_1, testFile); spy.replay(); SuiteEventDemuxer results = testBench.run(testClass); results.visitAllRuns(listener); spy.verify(); } @Test public void if_test_class_annotated_with_RunInDriverThread_then_does_not_use_the_Executor() { RunListener runListener = mock(RunListener.class); Executor executor = mock(Executor.class); Class<AnnotatedWithRunInDriverThreadTest> testClass = AnnotatedWithRunInDriverThreadTest.class; SuiteNotifier notifier = new ThreadBoundSuiteNotifier(ActorRef.wrap(runListener), new RunIdSequence(), new OutputCapturer()); DriverRunner driverRunner = new DriverRunner(new SimpleUnit(), testClass, notifier, executor); driverRunner.run(); verifyZeroInteractions(executor); // should anyways run the tests verify(runListener).onTestStarted(new RunId(1), TestId.ROOT); verify(runListener).onTestStarted(new RunId(1), TestId.of(0)); } // guinea pigs @RunVia(SimpleUnit.class) @SuppressWarnings({"UnusedDeclaration"}) public static class OnePassingTest { public void testPassing() { } public void unrelatedMethod() { // doesn't start with "test", so is not a test } } @RunVia(SimpleUnit.class) @SuppressWarnings({"UnusedDeclaration"}) public static class OneFailingTest { public void testFailing() { throw new AssertionError("dummy failure"); } } @RunVia(SimpleUnit.class) @SuppressWarnings({"UnusedDeclaration"}) public static class FailureInConstructorTest { public FailureInConstructorTest() { throw new RuntimeException("dummy exception"); } public void testNotExecuted() { } } @RunVia(SimpleUnit.class) @SuppressWarnings({"UnusedDeclaration"}) public static class IllegalTestMethodSignatureTest { public void testMethodWithParameters(Object illegal) { } } @RunVia(SimpleUnit.class) @SuppressWarnings({"UnusedDeclaration"}) public static class NoTestMethodsTest { } @RunVia(SimpleUnit.class) @SuppressWarnings({"UnusedDeclaration"}) @SimpleUnit.RunInDriverThread public static class AnnotatedWithRunInDriverThreadTest { public void testUnimportant() { } } }