[JAVA] Use constructor with arguments in cucumber-picocontainer

I am studying Cucumber in The Cucumber for Java Book, but there is an argument for the object to be injected in PicoContainer. I didn't write how to generate it with the constructor of, so I looked it up. (Actually, the latest Active JDBC (1.4.13) didn't work with the book sample (it worked with the same version of 1.4.1 as the book), so I needed an alternative.)

Thing you want to do

This is a partial modification of the sample in the above book, but I want to DI the Account object with cumcumber-picocontainer in Step Definition, but I need to specify the argument in the constructor when creating the Account object.

Account.java


public class Account extends Model {
	public Account() {
	}
	
	public Account(int number) {
		setInteger("number", number);
		setString("balance", "0.00");
		saveIt();
	}
...

AccountSteps.java


public class AccountSteps {
	private Account account; 
	
	public AccountSteps(Account account) {
		this.account = account;
	}
...

In the book, the same thing is achieved by creating a child class and DI it as shown below, instead of using the constructor with arguments directly.

TestAccount.java


public class TestAccount extends Account {
	public TestAccount() {
		super(1234);
	}
...

AccountSteps.java


public class AccountSteps {
	private TestAccount account; 
	
	public AccountSteps(TestAccount account) {
		this.account = account;
	}
...

How can I directly DI the Account object created by the constructor with arguments without doing that?

If you use PicoContainer directly

If you use PicoContainer directly instead of from Cucumber, you can create the object you want to DI yourself, or you can set it to create the object using a constructor with arguments.

MutablePicoContainer pico = new DefaultPicoContainer();

pico.addComponent(new Account(5)); //Create Account object in constructor with arguments
//Alternatively, specify the argument of the constructor
pico.addComponent(Account.class, Account.class, new Parameter[] { new ConstantParameter(new Integer(5)) });

Account account = pico.getComponent(Account.class);
System.out.println("number = " + account.getNumber());

When executed from Cucumber, MutablePicoContainer is generated by Cucumber and also sets the components to DI, so it is easy for the normal user to do nothing, but on the contrary I want to set it myself In that case, there is no way to get the MutablePicoContainer from Cucumber, so nothing can be done.

Although it is possible to set a DI container to some extent with Cucumber. .. ..

There was of course a similar request (Ability to configure PicoContainer), and the ObjectFactory API became available in response. But what you can do with this API is

pico.addComponent(Account.class)

(Although this makes sense because it allows you to specify which implementation class to use for Interface).

pico.addComponent(new Account(5));
pico.addComponent(Account.class, Account.class, new Parameter[] { new ConstantParameter(new Integer(5)) });

It seems that it is not possible at present to register an instance or specify an argument of the constructor like.

Then what should I do?

In fact, if the argument is a regular class instead of an int as in this example, you can do it normally. As a DI beginner, it took me a while to notice this. Since cucumber-picocontainer is clever, it will DI the object of the constructor argument without saying anything. So, it's a bit dirty implementation, but if you create a class (AccountNumber) for the argument of the constructor, you can do what you want.

Account.java


public class Account extends Model {
	public Account() {
	}
	
	public Account(int number) {
		setInteger("number", number);
		setString("balance", "0.00");
		saveIt();
	}
	//Constructor for DI
	public Account(AccountNumber number) {
		this(number.getNumber());
	}
...

AccountNumber.java


public class AccountNumber {
	private int number;
	public AccountNumber() {
		number = 1234;
	}
	public int getNumber() {
		return number;
	}
}

You can now pass the Account object you created with the arguments without changing anything in the Step Definition.

AccountSteps.java


public class AccountSteps {
	private Account account; 
	
	public AccountSteps(Account account) {
		this.account = account;
	}
...

Recommended Posts

Use constructor with arguments in cucumber-picocontainer
Use JDBC Manager with the settings in jdbc.dicon.
With Tomcat you can use placeholders ($ {...}) in web.xml
[JAVA] [Spring] [MyBatis] Use IN () with SQL Builder
Check method call arguments in blocks with RSpec
Use ProGuard with Gradle
Use java.time in Jackson
How to use Z3 library in Scala with Eclipse
Use Puphpeteer with Docker
Use XVim2 with Xcode 12.0.1
Use CentOS with LXD
Use Interceptor in Spring
Use OpenCV in Java
Use ngrok with Docker
Use webmock with Rspec
Use MouseListener in Processing
Use images in Rails
How to use JDD library in Scala with Eclipse
Use PostgreSQL in Scala
Use WebJars with Gradle
Use PreparedStatement in Java
Use jlink with gradle
Use Coveralls with GitHub Actions in a Ruby repository
Use thymeleaf3 with parent without specifying spring-boot-starter-parent in Spring Boot
Use your own classes in the lib directory with Rails6
Use Lambda Layers with Java
Use GDAL with Python with Docker
Use Thymeleaf with Azure Functions
Variadic arguments in various languages
Use pfx certificate with Okhttp3
Japaneseize using i18n with Rails
Use Bulk API with RestHighLevelClient
Use ruby variables in javascript.
Use SDKMAN! With Git Bash
Mock the constructor with PowerMock
Use multiple databases with Rails 6.0
Use Spring JDBC with Spring Boot
Use Redis Stream in Java
Use multiple checkboxes in Rails6!
Use Ruby with Google Colab
Use SpatiaLite with Java / JDBC
Use log4j2 with YAML + Gradle
[Docker] Use whenever with Docker + Rails
Use FIFO queues with recurring tasks in Elastic Beanstalk worker environments