RichardWarburton / lambda-behave

A modern testing and behavioural specification framework for Java 8
http://richardwarburton.github.io/lambda-behave/
MIT License
253 stars 52 forks source link

Add Spring-Integratiom #85

Open Writtscher opened 9 years ago

Writtscher commented 9 years ago

Enhacement:

I'd like to contribute and add ,as soon as I have time, a spring integration.

For example a LambdaBehaveSpringRunner that starts a spring context that provides to inject spring dependencies.

What you think?

RichardWarburton commented 9 years ago

I think some kind of spring integration would be good. I suspect a good idea would be to try and think up a few examples of what such an API would look like. Perhaps you can show some example code of what you would like it to look like?

Writtscher commented 9 years ago

I have implemented a first version. Have a look at it and share your thoughts. I had to copy paste some code of you as there is a lot of static. We should think about some inheritance..

Have a look lambda-behave-spring

(It's not final.. as it does not support every spring application context.. only tested with annotation...)

woprzech commented 7 years ago

Since Spring 4.2 you can use Spring Method Rule that allow you to be independent of any Runner.

sworisbreathing commented 7 years ago

I've tried @woprzech's suggestion but couldn't get that working with a @SpringBootTest annotated test class. Will try lambda-behave-spring.

sworisbreathing commented 7 years ago

ok so lambda-behave-spring didn't work for me because it only works with the @ContextConfiguration annotation. However I was able to get something working based on @woprzech's suggestion, using Spring Boot 1.5.7.RELEASE, JUnit 4.12, and lambda-behave 0.4.

In this case the application under test is a Spring Boot app exposing a simple REST API, tested with RestAssured. Here's the entire codebase (package names omitted):

Application.java:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

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

HelloWorldAPI.java:

import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/v1/hello-world-api")
public class HelloWorldAPI {

  @RequestMapping(value = "/hello-world", produces = MediaType.APPLICATION_JSON_VALUE)
  public ResponseEntity<?> helloWorld() {
    return ResponseEntity.ok("{\"message\": \"Hello, World!\"}");
  }
}

And finally the test class:

import java.lang.reflect.Method;

import com.insightfullogic.lambdabehave.JunitSuiteRunner;
import static com.insightfullogic.lambdabehave.Suite.describe;

import com.jayway.restassured.RestAssured;
import static com.jayway.restassured.RestAssured.when;
import com.jayway.restassured.response.Response;

import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.runner.RunWith;

import org.springframework.boot.test.context.SpringBootTest;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
import org.springframework.core.env.Environment;
import org.springframework.test.context.TestContextManager;
import org.springframework.test.context.junit4.rules.SpringClassRule;
import org.springframework.test.context.junit4.rules.SpringMethodRule;
import org.springframework.util.ReflectionUtils;

@RunWith(JunitSuiteRunner.class)
@SpringBootTest(webEnvironment = RANDOM_PORT)
public class HelloWorldAPITest {

  /*
   * The class and method rules work as an alternative to running with
   * SpringRunner/SpringJUnit4ClassRunner.
   *
   * You absolutely need both, and the class rule must be public, static, and final.
   */
  @ClassRule
  public static final SpringClassRule classRule = new SpringClassRule();

  @Rule
  public SpringMethodRule methodRule = new SpringMethodRule();

  /*
   * following the other lambda-behave examples, we use double-brace
   * initialization.
   */
  {
    /*
     * The first argument to describe MUST be the name of the class where the
     * SpringClassRule is defined (in other words, the test class).
     *
     * If we use an arbitrary string, the class rule will throw a
     * NullPointerException before the test executes.
     */
    describe(HelloWorldAPITest.class.getName(), it -> {

      /*
       * Here we are telling RestAssured what port to connect to.
       *
       * Unfortunately we don't have direct access to the test context that
       * Spring set up, so we will need to use a bit of reflection before we
       * can access that configuration property.
       */
      it.isSetupWith(() -> {
        Method getTestContextManagerMethod = ReflectionUtils.findMethod(SpringClassRule.class, "getTestContextManager", Class.class);
        ReflectionUtils.makeAccessible(getTestContextManagerMethod);
        TestContextManager manager = (TestContextManager) getTestContextManagerMethod.invoke(classRule, HelloWorldAPITest.class);
        Environment env = manager.getTestContext().getApplicationContext().getEnvironment();
        int port = env.getProperty("local.server.port", Integer.class);
        RestAssured.port = port;
      });

      // Test execution goes here.
      it.should("work", expect -> {
        Response response = when().get("/v1/hello-world-api/hello-world");

        expect.that(response.statusCode()).isEqualTo(200);
        expect.that(response.body().<String>path("message")).isEqualTo("Hello, World!");
      });
    });
  }

}