I am trying to write functional-test case for a rest controller. According to plan I'd like to start application and using TestRestTemplate call appropriate endpoint. Unfortunately, I am unable to do so because occurring error:
Cannot invoke method exchange() on null object
java.lang.NullPointerException: Cannot invoke method exchange() on null object
...
I can't figure out why and it is the only error message that I get. I feel confused ://
In addition, I'm using Spock framework for testing.
Thanks for your help in advance.
gradle.build
plugins {
id 'org.springframework.boot' version '2.6.3'
id 'org.unbroken-dome.test-sets' version '4.0.0'
id 'java'
id 'groovy'
id 'maven-publish'
}
apply from: "${rootDir}/gradle/test.gradle"
repositories {
mavenCentral()
}
dependencies {
implementation(group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '2.6.3')
testImplementation(group: 'org.codehaus.groovy', name: 'groovy-all', version: '3.0.9')
testImplementation(group: 'org.spockframework', name: 'spock-core', version: '2.0-groovy-3.0')
testImplementation(group: 'org.springframework.boot', name: 'spring-boot-starter-test', version: '2.6.3')
}
// some more usual Gradle stuff
test.gradle
testSets {
integrationTest { dirName = 'integration-test' }
functionalTest { dirName = 'functional-test' }
}
integrationTest {
mustRunAfter(test)
}
check.dependsOn(integrationTest)
functionalTest {
mustRunAfter(integrationTest)
}
check.dependsOn(functionalTest)
tasks.withType(Test) {
useJUnitPlatform()
}
GreetingControllerSpec
package tchorzyksen
/* imports */
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class GreetingControllerSpec extends Specification {
#Autowired
private TestRestTemplate testRestTemplate
void "get greeting"() {
when:
ResponseEntity<GreetingEntity> response = get("/greeting", GreetingEntity.class)
then:
response.getStatusCode() == HttpStatus.OK
}
protected <T> ResponseEntity<T> get(String uri, Class<T> responseClass) {
return testRestTemplate.exchange(uri, HttpMethod.GET, null, responseClass, [:])
}
}
MyWs.java
package tchorzyksen;
/* imports */
#SpringBootApplication
public class MyWs {
public static void main(String[] args) {
SpringApplication.run(MyWs.class, args);
}
}
and GreetingController
package tchorzyksen.ui.model.controller;
/* imports */
#RestController
public class GreetingController {
private static final String template = "Hello, %s!";
private final AtomicLong counter = new AtomicLong();
#GetMapping("/greeting")
public ResponseEntity<GreetingEntity> greeting(
#RequestParam(value = "name", defaultValue = "World") String name) {
return ResponseEntity.ok(
new GreetingEntity(counter.incrementAndGet(), String.format(template, name)));
}
}
Instead
#Autowired
private TestRestTemplate testRestTemplate
Enough
private TestRestTemplate testRestTemplate = new TestRestTemplate();
For all people interested in solution to this question. We are missing one dependency here
testImplementation group: 'org.spockframework', name: 'spock-spring', version: '2.1-M2-groovy-3.0'
Which basically makes it possible to write tests in Spock and use spring test context.
Save yourselfs guys it took me 4 hours to discover (very sad face) ://
Related
I have this (working) code, which is extended from the gradle documentation
https://docs.gradle.org/4.10.3/userguide/custom_plugins.html#sec:implementing_a_dsl
class User {
String name
}
class Group {
User user
}
class GreetingPluginExtension {
String message
final Group group
#javax.inject.Inject
GreetingPluginExtension(ObjectFactory objectFactory) {
group = objectFactory.newInstance(Group)
group.user = objectFactory.newInstance(User)
}
void group(Action<? super Group> action) {
action.execute(group)
}
void user(Action<? super User> action) {
action.execute(group.user)
}
}
class GreetingPlugin implements Plugin<Project> {
void apply(Project project) {
// Create the extension, passing in an ObjectFactory for it to use
def extension = project.extensions.create('greeting', GreetingPluginExtension, project.objects)
project.task('hello') {
doLast {
println "${extension.message} from ${extension.group.user.name}"
}
}
}
}
The configuration closure looks like this:
greeting {
message = 'Hello'
group {
user {
name = 'tom'
}
}
}
But I would like to have a List of users an I tried:
class User {
String name
}
class Group {
ArrayList<User> users
}
class GreetingPluginExtension {
String message
final Group group
#javax.inject.Inject
GreetingPluginExtension(ObjectFactory objectFactory) {
// Create a Person instance
group = objectFactory.newInstance(Group)
group.users = []
}
void group(Action<? super Group> action) {
action.execute(group)
}
void users(Action<? super ArrayList<User>> action) {
action.execute(group.users)
}
}
class GreetingPlugin implements Plugin<Project> {
void apply(Project project) {
// Create the extension, passing in an ObjectFactory for it to use
def extension = project.extensions.create('greeting', GreetingPluginExtension, project.objects)
project.task('hello') {
doLast {
extension.group.users.each {
println "${extension.message} from ${it.name}"
}
}
}
}
}
with this closure:
greeting {
message = 'Hello'
group {
users = [
{name = 'tom'} ,
{name = 'tim'}
]
}
}
my output is the following:
Hello from myProjectName
Hello from myProjectName
which is not the expected output but the rootProject.name
The output has the right number of elements, but is not referenced to the user.
How can I fix this? I would also appreciate informations about other approaches to map nested objects (and lists) into the extension-setting.
Greetings Tom
The following slightly modified version I believe does what you intended:
apply plugin: GreetingPlugin
greeting {
message = 'Hello'
group {
user(name: 'tom')
user(name: 'tim')
}
}
class User {
String name
}
class Group {
ArrayList<User> users = []
def user(props) {
users << new User(props)
}
}
class GreetingPluginExtension {
String message
final Group group
#javax.inject.Inject
GreetingPluginExtension(ObjectFactory objectFactory) {
// Create a Person instance
group = objectFactory.newInstance(Group)
}
void group(Action<? super Group> action) {
action.execute(group)
}
}
class GreetingPlugin implements Plugin<Project> {
void apply(Project project) {
// Create the extension, passing in an ObjectFactory for it to use
def extension = project.extensions.create('greeting', GreetingPluginExtension, project.objects)
project.task('hello') {
doLast {
extension.group.users.each {
println "${extension.message} from ${it.name}"
}
}
}
}
}
When run, it prints:
~> gradle hello
> Task :hello
Hello from tom
Hello from tim
BUILD SUCCESSFUL in 0s
1 actionable task: 1 executed
Note that I have removed the method:
void users(Action<? super ArrayList<User>> action)
as it is not needed for the above to work. I also changed the dsl somewhat. I actually think the changed dsl looks more readable and idiomatic but this is of course a matter of taste.
Note also that if you want to send in more properties to the user (say email), you can do this without modifying the scaffolding code, i.e.:
greeting {
message = 'Hello'
group {
user(name: 'tom', email: 'tom#wonderland.org')
user(name: 'tim', email: 'tim#wonderland.org')
}
}
class User {
String name
String email
}
If you were specifically looking for how you make a doubly nested configuration closure for a collection work, this does not solve your problem, but it does give you a reasonably clean way of accomplishing what the build script intended.
I am trying to write my own gradle plugin and it needs to be able to configure a set of objects - how many of these objects and what they're called is up to the user.
The doco for creating custom gradle plugins with advanced customisability is quite poor. It mentions project.container() method to do this kind of thing, but I couldn't figure out how to make it work in my usecase.
This is an example of my plugin's configuration DSL as it stands:
teregrin {
terraformVersion = '0.6.6'
root("dev"){
accessKey = "flobble"
}
root("prd"){
}
}
And this is my plugin extension object that allows me to configure it:
class TeregrinPluginExtension {
boolean debug = false
boolean forceUnzip = false
String terraformVersion = null
Set<TeregrinRoot> roots = []
def root(String name, Closure c){
def newRoot = new TeregrinRoot(name)
c.setDelegate(newRoot)
c()
roots << newRoot
}
}
The extensions wired up in my plugin in the standard way:
project.extensions.create("teregrin", TeregrinPluginExtension)
This works ok, but it's a pretty ugly configuration style, not really in the style of the typical gradle DSL.
How can I change my plugin configuration DSL to be something like this:
teregrin {
terraformVersion = '0.6.6'
roots {
dev {
accessKey = "flobble"
}
prd {
}
}
}
The gradle way of implementing such DSL is by using extensions and containers:
apply plugin: SamplePlugin
whatever {
whateverVersion = '0.6.6'
conf {
dev {}
qa {}
prod {
accessKey = 'prod'
}
}
}
task printWhatever << {
println whatever.whateverVersion
whatever.conf.each { c ->
println "$c.name -> $c.accessKey"
}
}
class SamplePlugin implements Plugin<Project> {
void apply(Project project) {
project.extensions.create('whatever', SampleWhatever)
project.whatever.extensions.conf = project.container(SampleConf)
project.whatever.conf.all {
accessKey = 'dev'
}
}
}
class SampleWhatever {
String whateverVersion
}
class SampleConf {
final String name
String accessKey
SampleConf(String name) {
this.name = name
}
}
while groovy way of doing implementing such DSL is meta programming - you need to implement methodMissing in this particular case. Below is a very simple example that demonstrates how it works:
class SomeExtension {
def devConf = new SomeExtensionConf()
void methodMissing(String name, args) {
if ('dev'.equals(name)) {
def c = args[0]
c.resolveStrategy = Closure.DELEGATE_FIRST
c.delegate = devConf
c()
} else {
throw new MissingMethodException("Could not find $name method")
}
}
def getDev() {
devConf
}
}
class SomeExtensionConf {
def accessKey
}
project.extensions.create('some', SomeExtension)
some {
dev {
accessKey = 'lol'
}
}
assert 'lol'.equals(some.dev.accessKey)
Of course it has no error checking - so the args size and type of each argument need to be validated - it's omitted for the sake of brevity.
Of course there's no need to create a separate class for each configuration (I mean dev, prod, etc.). Create a single class that holds configuration and store them all in a Map where key is configuration name.
You can find a demo here.
My gradle plugin generates a number of tasks that have shared configuration. This configuration needs to be marked as #Input, so when it's changed, the task is marked as stale and re-evaluated. I'm finding it challenging to share the config when it should apply to multiple tasks. I'm using avoiding project.afterEvaluate to allow incremental compilation. This example is a reduced version of what I currently have:
Current Plugin Code:
class MyPluginTaskOne extends DefaultTask {
#Input config = "default"
#TaskAction
public void action() {
// something that depends on config
}
}
class MyPluginTaskTwo extends DefaultTask {
#Input config = "default"
#TaskAction
public void action() {
// something that depends on config
}
}
class MyPluginExtension {
// blank for now
}
class MyPlugin implements Plugin<Project> {
void apply(Project project) {
project.extensions.create("myPluginConfig", MyPluginExtension)
project.tasks.create(name: 'myPluginTaskOne', type: MyPluginTaskOne) {}
project.tasks.create(name: 'myPluginTaskTwo', type: MyPluginTaskTwo) {}
}
}
Current Config:
Currently the best way I have to share state is the following. This has the problem is that it's error prone and doesn't automatically sharing the setting:
apply plugin: MyPlugin
// Kludgy way of sharing configuration across two tasks:
def sharedConfig = "SHARED-CONFIG"
myPluginTaskOne {
config sharedConfig
}
myPluginTaskTwo {
config sharedConfig
}
Preferred Config:
What I'd like to do is a configuration something like the following, but with all the benefits of tracking #Input dependencies and up-to-date tests.
myPluginConfig {
config "SHARED-CONFIG"
// myPluginTaskOne and myPluginTaskTwo both gets automatic
// 'SHARED-CONFIG' through Gradle
}
It appears that you can automatically add dependencies between tasks (see below). Is it possible to configure only the first task and then have that #Input trickle down to the #Input on the second task?
Let us try removing the task dependencies by relying on how CopySpec.from() evaluates arguments with Project.files(). Gradle can automatically add task dependencies for us. This also adds the output of the generator task as inputs to the zip task.
From https://gradle.org/feature-spotlight-incremental-builds/
To build on Mark's comments. Here is an example of a property that applies to all tasks and cannot be overridden (config).
class MyPluginTaskOne extends DefaultTask {
#Input String getConfig() { project.myPluginConfig.config }
#TaskAction
public void action() {
// something that depends on config
}
}
class MyPluginTaskTwo extends DefaultTask {
#Input String getConfig() { project.myPluginConfig.config }
#TaskAction
public void action() {
// something that depends on config
}
}
class MyPluginExtension {
String config
}
class MyPlugin implements Plugin<Project> {
void apply(Project project) {
project.with {
extensions.create("myPluginConfig", MyPluginExtension)
tasks.create(name: 'myPluginTaskOne', type: MyPluginTaskOne) {}
tasks.create(name: 'myPluginTaskTwo', type: MyPluginTaskTwo) {}
}
}
}
The most common convention is to use an extension to do this. It looks like you've started to do this. You would then define the property on the extension, then your plugin would read the extension and set the property on all relevant tasks.
myPluginConfig {
sharedConfig 'value'
}
In your plugin:
def extension = extensions.create("myPluginConfig", MyPluginExtension)
project.afterEvaluate {
// read prop from `extension` and set prop on tasks
}
I have a Person and a PersonViewModel. I created a map from Person => PersonViewModel. The problem is that PersonViewModel's only constructor needs an argument (it has a dependency that I want to be injected) and AutoMapper is complaining because it says it needs a parameterless constructor.
To fix it, I used the ConstructServicesUsing method, but I haven't been successful with it :(
To illustrate the case, I created a test for you to see what I'm doing. It's pretty simple:
[TestMethod]
public void TestConstructServicesUsing()
{
Mapper.Initialize(configuration =>
{
configuration.ConstructServicesUsing(FactoryMethod);
configuration.CreateMap<Person, PersonViewModel>();
});
Mapper.AssertConfigurationIsValid();
var person = new Person();
var personViewModel = Mapper.Map<Person, PersonViewModel>(person);
}
private object FactoryMethod(Type type)
{
throw new NotImplementedException();
}
}
The rest of the code is the classes and interface definitions. They are almost empty.
public class SomeyDependency : ISomeDependency
{
}
public class PersonViewModel
{
private readonly ISomeDependency service;
public PersonViewModel(ISomeDependency service)
{
this.service = service;
}
public string Name { get; set; }
}
public class Person
{
public string Name { get; set; }
}
public interface ISomeDependency
{
}
As you see, I provide AutoMapper with a FactoryMethod, but it never get called.
When it reaches the last line of the test (Mapper.Map<...>()) it throws an excepton saying:
AutoMapper.AutoMapperMappingException:
Mapping types:
Person -> PersonViewModel
MappingWithContainerTests.Person -> MappingWithContainerTests.PersonViewModel
Destination path:
PersonViewModel
Source value:
MappingWithContainerTests.Person ---> System.ArgumentException: Type needs to have a constructor with 0 args or only optional args
Parameter name: type
What's the problem?
Why isn't the FactoryMethod being called?
As #khorvat mention where is missing .ConstructUsingServiceLocator(), for concrete mapping.
Also you can set constructor directly by
.ConstructUsing(source => Method(source.anySourceOptions))
Or as exception said:
PersonViewModel, must have a constructor with 0 args or only optional
args. You have only one constructor with 1 not optional argument
you may create one more constructor without args:
public PersonViewModel()
{
this.service = new SomeDependency();
}
I'm using .NET Core 3.1 and Automapper.Extensions.Microsoft.DependencyInjection.
This does not work for me (Same error as yours):
public class AutoMapping : Profile
{
public AutoMapping()
{
CreateMap<Context, MainViewModel>()
.ReverseMap()
.ConstructUsingServiceLocator();
}
}
But this does work:
public class AutoMapping : Profile
{
public AutoMapping()
{
CreateMap<Context, MainViewModel>()
.ConstructUsingServiceLocator()
.ReverseMap();
}
}
I still do not fully understand the cause.
This is my test
[TestClass]
public class RepositoryTests
{
private APurchaseOrderRepository _repository;
[TestInitialize]
public void TestInitialize()
{
_repository = new FakePurchaseOrderRepository();
}
[TestMethod]
public void RepositoryGetPurchaseOrdersForStoreCallsValidatePurchaseOrders()
{
var store = new Store();
var mockRepo = new Mock<APurchaseOrderRepository>();
mockRepo.Protected().Setup("ValidatePurchaseOrders", ItExpr.IsAny<List<PurchaseOrder>>());
_repository.GetPurchaseOrders(store);
mockRepo.Protected().Verify("ValidatePurchaseOrders", Times.Once(), ItExpr.IsAny<List<PurchaseOrder>>());
}
}
APurchaseOrderRepository and it's interface look like this
public interface IPurchaseOrderRepository
{
List<PurchaseOrder> GetPurchaseOrders(Store store);
}
public abstract class APurchaseOrderRepository : IPurchaseOrderRepository
{
public abstract List<PurchaseOrder> GetPurchaseOrders(Store store);
protected virtual bool ValidatePurchaseOrders(List<PurchaseOrder> purchaseOrders)
{
return true;
}
}
And my Fake
public class FakePurchaseOrderRepository : APurchaseOrderRepository
{
public override List<PurchaseOrder> GetPurchaseOrders(Store store)
{
var purchaseOrders = new List<PurchaseOrder>();
ValidatePurchaseOrders(purchaseOrders);
return purchaseOrders;
}
}
However, my test fails with:
Test method
PreSwapTests.RepositoryTests.RepositoryGetPurchaseOrdersForStoreCallsValidatePurchaseOrders
threw exception: Moq.MockException: Expected invocation on the mock
once, but was 0 times: mock =>
mock.ValidatePurchaseOrders(It.IsAny())
Configured setups: mock =>
mock.ValidatePurchaseOrders(It.IsAny()), Times.Never No
invocations performed.
What am I doing wrong?
Notes:
Moq.4.0.10827
Update:
I think it is this line mockRepo.Protected().Setup("ValidatePurchaseOrders");, because I need to add the parameters to it as a second argument, but I can't seem to get it correct.
Update 2:
Made some modifications, now it compiles, but isn't counting correctly...or something, error message and code are both updated above.
Realized I was doing this all wrong, changed my objects to work with this test
[TestMethod]
public void RepositoryGetPurchaseOrdersForStoreCallsValidatePurchaseOrders()
{
var store = new Store();
var mockPurchaseOrderProvider = new Mock<IPurchaseOrderProvider>();
var mockPurchaseOrderValidator = new Mock<IPurchaseOrderValidator>();
var purchaseOrderRepository = new PurchaseOrderRepository(mockPurchaseOrderProvider.Object, mockPurchaseOrderValidator.Object);
mockPurchaseOrderValidator.Setup(x => x.ValidatePurchaseOrders(It.IsAny<List<PurchaseOrder>>()));
purchaseOrderRepository.GetPurchaseOrders(store);
mockPurchaseOrderValidator.Verify(x => x.ValidatePurchaseOrders(It.IsAny<List<PurchaseOrder>>()), Times.Once());
}
This is a much better structure now I think.
It's because ValidatePurchaseOrders is not in your IPurchaseOrderRepository interface.
The repository is declared as private IPurchaseOrderRepository _repository; so it can only see what is in the interface.