英文:
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首先检查编写器是否已初始化:
    @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的方式并不允许标记已初始化:
	// 返回表示状态的对象。
	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<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);
   }
}
and I am writing a simple unit test without Spring, something like :
    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();
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 :
    @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");
		}
...
but the way OutputState is built doesn't give a chance to say it has been initiated :
	// Returns object representing state.
	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;
	}
--> 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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论