unit testing a FlatFileItemWriter outside of Spring – "Writer must be open before it can be written to" exception

huangapple go评论61阅读模式
英文:

unit testing a FlatFileItemWriter outside of Spring - "Writer must be open before it can be written to" exception

问题

以下是您要翻译的内容:

我正在编写一个简单的批处理程序,用于编写CSV文件,我想使用Spring Batch的FlatFileItemWriter来实现,使用的是Spring Boot 2.3.1.RELEASE版本。

我想对编写器进行单元测试,以确认它已正确配置。

代码非常简单:

    public class CSVResultWriter implements ItemWriter<Project> {

    private final FlatFileItemWriter writer;

    public CSVResultWriter(String outputResource) {

      writer=new FlatFileItemWriterBuilder<Project>()
          .name("itemWriter")
          .resource(new FileSystemResource(outputResource))
          .lineAggregator(new PassThroughLineAggregator<>())
          .append(true)
          .build();

    }

   @Override
   public void write(List<? extends Project> items) throws Exception {
     writer.write(items);
   }
}

我正在编写一个简单的单元测试,不使用Spring,类似于:

    File generatedCsvFile = new File(workingDir.toString() + File.separator + "outputData.csv");

    CSVResultWriter writer = new CSVResultWriter(generatedCsvFile.getAbsolutePath());

    Project sampleProject = Project.builder().name("sampleProject1").build();

    writer.write(List.of(sampleProject));

    assertThat(generatedCsvFile).exists();

但是测试失败,显示错误:

org.springframework.batch.item.WriterNotOpenException: Writer must be open before it can be written to

查看Spring源代码,我不明白如何使其工作。在尝试写入项目时,Spring首先检查编写器是否已初始化:

https://github.com/spring-projects/spring-batch/blob/744d1834fe313204f06c0bcd0eedd472ab4af6be/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/AbstractFileItemWriter.java#L237

    @Override
	public void write(List<? extends T> items) throws Exception {
		if (!getOutputState().isInitialized()) {
			throw new WriterNotOpenException("Writer must be open before it can be written to");
		}
...

但是构建OutputState的方式并不允许标记已初始化:

https://github.com/spring-projects/spring-batch/blob/744d1834fe313204f06c0bcd0eedd472ab4af6be/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/AbstractFileItemWriter.java#L363

	// 返回表示状态的对象。
	protected OutputState getOutputState() {
		if (state == null) {
			File file;
			try {
				file = resource.getFile();
			}
			catch (IOException e) {
				throw new ItemStreamException("Could not convert resource to file: [" + resource + "]", e);
			}
			Assert.state(!file.exists() || file.canWrite(), "Resource is not writable: [" + resource + "]");
			state = new OutputState();
			state.setDeleteIfExists(shouldDeleteIfExists);
			state.setAppendAllowed(append);
			state.setEncoding(encoding);
		}
		return state;
	}

---> OutputState中的initialized标志保持其默认值,即false。

所以我有点困惑... 我猜当Spring管理时,会发生一些魔法,它就可以工作。

我是否遗漏了一些明显的东西,或者我们真的无法在Spring之外进行此测试?

英文:

I am writing a simple batch that writes a CSV file, and I wanted to use Spring Batch FlatFileItemWriter for that, using Spring Boot 2.3.1.RELEASE.

I want to unit test my writer so that I can confirm it's configured properly.

the code is very simple :

    public class CSVResultWriter implements ItemWriter&lt;Project&gt; {

    private final FlatFileItemWriter writer;

    public CSVResultWriter(String outputResource) {

      writer=new FlatFileItemWriterBuilder&lt;Project&gt;()
          .name(&quot;itemWriter&quot;)
          .resource(new FileSystemResource(outputResource))
          .lineAggregator(new PassThroughLineAggregator&lt;&gt;())
          .append(true)
          .build();

    }

   @Override
   public void write(List&lt;? extends Project&gt; items) throws Exception {
     writer.write(items);
   }
}

and I am writing a simple unit test without Spring, something like :

    File generatedCsvFile = new File(workingDir.toString() + File.separator + &quot;outputData.csv&quot;);

    CSVResultWriter writer = new CSVResultWriter(generatedCsvFile.getAbsolutePath());

    Project sampleProject = Project.builder().name(&quot;sampleProject1&quot;).build();

    writer.write(List.of(sampleProject));

    assertThat(generatedCsvFile).exists();

But the test fails saying :

org.springframework.batch.item.WriterNotOpenException: Writer must be open before it can be written to

Looking at Spring source code, I don't understand how it's possible to make it work.. When trying to write items, the first thing that Spring does is checking that the writer is initialized :

https://github.com/spring-projects/spring-batch/blob/744d1834fe313204f06c0bcd0eedd472ab4af6be/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/AbstractFileItemWriter.java#L237

    @Override
	public void write(List&lt;? extends T&gt; items) throws Exception {
		if (!getOutputState().isInitialized()) {
			throw new WriterNotOpenException(&quot;Writer must be open before it can be written to&quot;);
		}
...

but the way OutputState is built doesn't give a chance to say it has been initiated :

https://github.com/spring-projects/spring-batch/blob/744d1834fe313204f06c0bcd0eedd472ab4af6be/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/support/AbstractFileItemWriter.java#L363

	// Returns object representing state.
	protected OutputState getOutputState() {
		if (state == null) {
			File file;
			try {
				file = resource.getFile();
			}
			catch (IOException e) {
				throw new ItemStreamException(&quot;Could not convert resource to file: [&quot; + resource + &quot;]&quot;, e);
			}
			Assert.state(!file.exists() || file.canWrite(), &quot;Resource is not writable: [&quot; + resource + &quot;]&quot;);
			state = new OutputState();
			state.setDeleteIfExists(shouldDeleteIfExists);
			state.setAppendAllowed(append);
			state.setEncoding(encoding);
		}
		return state;
	}

--> the initialized flag in OutputState keeps its default value, which is false.

So I am a bit puzzled.. I guess when this is managed by Spring, some magic happens and it works.

Am I missing something obvious, or can't we really test this outside of Spring ?

答案1

得分: 2

FlatFileItemWriter 实现了 ItemStream 接口,当在 Spring Batch 作业中使用时,会自动遵守该接口的约定。

如果您想在 Spring 之外使用该写入器,您需要手动调用这些方法(open/update/close)。在参考文档的 Item Stream 部分中有提到:

如果同时实现了 ItemStream 的 ItemReader 的客户端,在调用 read 之前应调用 open,以打开诸如文件或获取连接之类的任何资源。
同样的限制也适用于实现了 ItemStream 的 ItemWriter。
英文:

The FlatFileItemWriter implements the ItemStream contract, which will be automatically honored when used in a Spring Batch job.

If you want to use the writer outside of Spring, you need to call theses methods (open/update/close) manually. This is mentioned in the Item Stream section of the reference docs:

Clients of an ItemReader that also implement ItemStream should call open before any calls
to read, in order to open any resources such as files or to obtain connections.
A similar restriction applies to an ItemWriter that implements ItemStream

huangapple
  • 本文由 发表于 2020年8月26日 18:54:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/63596068.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定