I am looking for a way in the Dart programming language to redirect the
output from stdout, into a "Something", that I can call .toString() on, and get
anything printed on stdout as String. This is useful for unit tests.
Currently I wrap stdout in my Display class, and store the text for one call and verify that stdout's write is
used. See also my side note below, how it can be done in Java.
import 'dart:io';
import 'package:mockito/mockito.dart';
import 'package:test/test.dart';
class Display {
IOSink output;
String lastTextPrinted;
Display(IOSink output) {
this.output = output;
}
void myPrint(String text) {
lastTextPrinted = text;
output.write(text);
}
}
main() {
test('prints hello world', () {
Display display = new Display(stdout);
display.myPrint("Hello world!");
expect("Hello world!", display.lastTextPrinted);
});
test('myPrint calls ioSinks write', () {
MockIOSink ioSink = new MockIOSink();
Display display = Display(ioSink);
display.myPrint("Hello world!");
verify(ioSink.write("Hello world!"));
});
}
class MockIOSink extends Mock implements IOSink {}
Sidenote: In Java with JUnit 4 I used something similar to this:
There System.out is a PrintStream and I can assign my own Stream to it, for instance ByteArrayOutPutStream.
The latter has a .toString method I can call to verify its content against.
#Test
public void testMyMessageToDisplay() throws Exception
{
ByteArrayOutputStream canvas = new ByteArrayOutputStream();
System.setOut(new PrintStream(canvas);
new Display().displayMyMessage("Hello world!");
Assert.assertEquals("Hello World!", canvas.toString("UTF-8"));
}
public static class Display {
public void displayMyMessage(String myMessage) {
System.out.println(myMessage);
}
}
Look at https://pub.dartlang.org/packages/test_process – it gives a lot of nice methods for verifying the output of a process
See the example in the readme:
import 'package:test/test.dart';
import 'package:test_process/test_process.dart';
void main() {
test("pub get gets dependencies", () async {
var process = await TestProcess.start("pub", ["get"]);
// Each stream matcher will consume as many lines as it matches from a
// StreamQueue, and no more, so it's safe to use them in sequence.
await expectLater(process.stdout, emits("Resolving dependencies..."));
// The emitsThrough matcher matches and consumes any number of lines, as
// long as they end with one matching the argument.
await expectLater(process.stdout, emitsThrough("Got dependencies!"));
await process.shouldExit(0);
});
}
Related
I have the following code. Dictionary is just a wrapper for a List of type String.
public Dictionary getDictionary(int size, String text) {
return restTemplate.execute(url, HttpMethod.GET, null, response -> {
BufferedReader br = new BufferedReader(new InputStreamReader(response.getBody()));
List<String> words = new ArrayList<>();
String line;
while((line = br.readLine()) != null){
if (isMatch(line, size, text)){
words.add(line.toLowerCase());
}
}
br.close();
return new Dictionary(words);
});
}
private boolean isMatch(String word, int size, String text) {
if(word.length() != size) {
return false;
}
return wordUtil.isAnagram(word, text);
}
I'm having a hard time test this method at the moment. The HTTP call just returns a list of words in plain text with new line separators.
I want to write a test where I can stub the response.getBody().
I.e. I want response.getBody() to return a bunch of words, and I'll assert that the returned Dictionary only contains the words that are of size size and that are an anagram of the string text.
Is this possible?
Thanks
It is possible to stub a method taking a callback, and execute the callback when the stub is called.
The idea is to:
use when / thenAnswer to execute code when the stubbed method is called
use invocationOnMock passed to thenAnswer to get the callback instance
call the callback, providing necessary params
#Test
void testExecute() {
String responseBody = "line1\nline2";
InputStream responseBodyStream = new ByteArrayInputStream(responseBody.getBytes());
ClientHttpResponse httpResponse = new MockClientHttpResponse(responseBodyStream, 200);
when(restTemplate.execute(any(URI.class), eq(HttpMethod.GET), eq(null), any())).thenAnswer(
invocationOnMock -> {
ResponseExtractor<MyDictionary> responseExtractor = invocationOnMock.getArgument(3);
return responseExtractor.extractData(httpResponse);
}
);
MyDictionary ret = aController.getDictionary(1, "text");
// assert ret against your expecations
}
Having said that, this seems to be a bit complicated for the task at hand. IMHO you will be better off if you separate the logic of dealing with Http from your business logic. Extract a method taking your inputStream, and test that separately.
Here is a sample of the code flow:
Trigger the process with an API specifying bulkSize and totalRecords.
Use those parameters to acquire data from DB
Create a processor with the bulkSize.
Send both the data and processor into a method which:
-iterates over the resultset, assembles a JSON for each result, calls a method if the final JSON is not empty and adds that final JSON to the process using processor.add() method.
This is where the outcome of the code is split
After this, if the concurrentRequest parameter is 0 or 1 or any value < (totalRecords/bulkSize), the processor.add() line is where the code stalls and never continues to the next debug line.
However, when we increase the concurrentRequest parameter to a value > (totalRecords/bulkSize), the code is able to finish the .add() function and move onto the next line.
My reasoning leads me to believe we might be having issues with our BulkProcessListener which is making the .add() no close or finish like it is supposed to. I would really appreciate some more insight about this topic!
Here is the Listener we are using:
private class BulkProcessorListener implements Listener {
#Override
public void beforeBulk(long executionId, BulkRequest request) {
// Some log statements
}
#Override
public void afterBulk(long executionId, BulkRequest request, BulkResponse response) {
// More log statements
}
#Override
public void afterBulk(long executionId, BulkRequest request, Throwable failure) {
// Log statements
}
}
Here is the createProcessor():
public synchronized BulkProcessor createProcessor(int bulkActions) {
Builder builder = BulkProcessor.builder((request, bulkListener) -> {
long timeoutMin = 60L;
try {
request.timeout(TimeValue.timeValueMinutes(timeoutMin));
// Log statements
client.bulkAsync(request, RequestOptions.DEFAULT,new ResponseActionListener<BulkResponse>());
}catch(Exception ex) {
ex.printStackTrace();
}finally {
}
}, new BulkProcessorListener());
builder.setBulkActions(bulkActions);
builder.setBulkSize(new ByteSizeValue(buldSize, ByteSizeUnit.MB));
builder.setFlushInterval(TimeValue.timeValueSeconds(5));
builder.setConcurrentRequests(0);
builder.setBackoffPolicy(BackoffPolicy.noBackoff());
return builder.build();
}
Here is the method where we call processor.add():
#SuppressWarnings("deprecation")
private void addData(BulkProcessor processor, String indexName, JSONObject finalDataJSON, Map<String, String> previousUniqueObject) {
// Debug logs
processor.add(new IndexRequest(indexName, INDEX_TYPE,
previousUniqueObject.get(COMBINED_ID)).source(finalDataJSON.toString(), XContentType.JSON));
// Debug logs
}
I'm having trouble reading other Stack Overflow posts so after a few hours I'm looking for help.
I have two methods that I want to test. And I'd like to test the second one using Mock, but having trouble figuring out what to do.
Here's the first method:
String readFileContents(Path filePath) {
StringBuilder fileContents = new StringBuilder()
BufferedReader br = Files.newBufferedReader(filePath, StandardCharsets.UTF_8)
String line
while ((line = br.readLine()) != null) {
fileContents.append(line).append('\n')
}
fileContents
}
And I test it with
class CdmFileSpec extends Specification {
private CdmFile cdmFile
private static final String filePath = 'src/test/resources/cdm/test/cdmFileTestFile.txt'
void setup() {
cdmFile = new CdmFile()
}
void 'test noFileExists'() {
given:
Path notRealPath = Paths.get('src/test/resources/cdm//test/notreal.txt')
when:
String fileContents = cdmFile.readFileContents(notRealPath)
then:
thrown NoSuchFileException
}
void 'test readFileContents() reads file contents'() {
given:
Path testFilePath = Paths.get(filePath)
when:
String fileContents = cdmFile.readFileContents(testFilePath)
then:
fileContents.contains('hip hop horrayy\n\nhoooo\n\nheyyy\n\nhoooo')
}
}
This works as I've placed a real file in the filePath.
I'm wondering... how can I test the next method using Mock?
void eachLineInFileAsString(Path filePath,
#ClosureParams(value = SimpleType, options = ['java.lang.String'] )Closure applyLine) {
BufferedReader br = Files.newBufferedReader(filePath)
String line
while ((line = br.readLine()) != null) {
applyLine.call(line)
}
}
The problem with mocking in so many cases is that methods create their own dependencies instead of having them injected or calling a mockable service method creating them. I suggest you refactor your code just a little bit, extracting BufferedReader creation into a service method:
package de.scrum_master.stackoverflow.q56772468
import groovy.transform.stc.ClosureParams
import groovy.transform.stc.SimpleType
import java.nio.charset.StandardCharsets
import java.nio.file.Files
import java.nio.file.Path
class CdmFile {
String readFileContents(Path filePath) {
StringBuilder fileContents = new StringBuilder()
BufferedReader br = createBufferedReader(filePath)
String line
while ((line = br.readLine()) != null) {
fileContents.append(line).append('\n')
}
fileContents
}
void eachLineInFileAsString(
Path filePath,
#ClosureParams(value = SimpleType, options = ['java.lang.String']) Closure applyLine
) {
BufferedReader br = createBufferedReader(filePath)
String line
while ((line = br.readLine()) != null) {
applyLine.call(line)
}
}
protected BufferedReader createBufferedReader(Path filePath) {
Files.newBufferedReader(filePath, StandardCharsets.UTF_8)
}
}
Now mocking is quite simple and you don't even need your test resource file anymore (only if you want to do an integration test without mocks):
package de.scrum_master.stackoverflow.q56772468
import spock.lang.Specification
import java.nio.charset.StandardCharsets
import java.nio.file.NoSuchFileException
import java.nio.file.Path
import java.nio.file.Paths
class CmdFileTest extends Specification {
private static final String filePath = 'mock/cdmTestFile.txt'
private static final String fileContent = """
I heard, that you're settled down
That you found a girl and you're, married now
I heard, that your dreams came true
I guess she gave you things
I didn't give to you
""".stripIndent()
private CdmFile cdmFile
void setup() {
cdmFile = Spy() {
createBufferedReader(Paths.get(filePath)) >> {
new BufferedReader(
new InputStreamReader(
new ByteArrayInputStream(
fileContent.getBytes(StandardCharsets.UTF_8)
)
)
)
}
}
}
def "non-existent file leads to exception"() {
given:
Path notRealPath = Paths.get('notreal.txt')
when:
cdmFile.readFileContents(notRealPath)
then:
thrown NoSuchFileException
}
def "read file contents into a string"() {
given:
Path testFilePath = Paths.get(filePath)
when:
String fileContents = cdmFile.readFileContents(testFilePath)
then:
fileContents.contains("your dreams came true\nI guess")
}
def "handle file content line by line"() {
given:
def result = []
def closure = { line -> result << line }
Path testFilePath = Paths.get(filePath)
when:
cdmFile.eachLineInFileAsString(testFilePath, closure)
then:
result == fileContent.split("\n")
}
}
Please note that I am using a Spy() here, i.e. leaving the original CdmFile object intact and just stubbing the service method createBufferedReader(..) when called with exactly parameter Paths.get(filePath). For other paths the original method is called, which is important for the non-existent file test or if you want to add tests involving real resource file loading like in your own example.
Whenever it is difficult to test a class or component, difficult to inject mocks or otherwise isolate the subject under test, that is a reason to refactor your application code for better testability. When done right also it should also result in better separation of concerns and better componentisation. If your tests become very sophisticated, contrived, brittle and hard to understand and maintain, that is usually a smell and you ought to refactor the application code instead.
There's no need for a Mock, as you can just use a locally defined Closure:
def "test the method"() {
given:
def result = []
def closure = { line -> result << line }
Path testFilePath = Paths.get(filePath)
when:
eachLineInFileAsString(testFilePath, closure)
then: // I'm guessing here
result == [
'line 1',
'line 2',
'line 3',
'line 4'
]
}
I have some static method:
class WebUtils {
static httpPostRequest(String url, Map data, Map headers) {
//some code here
}
}
And service:
class ChatService {
void sendMessage(String text) {
//some preparing code
WebUtils.httpPostRequest(url, data, headers)
}
}
Now I want to check invocation of static method in the service by unit-test. Somehow like this:
void "test sending message"() {
given:
String text = 'Test'
def mockedWebUtils = Mock(WebUtils)
when:
service.sendMessage(message)
then:
1*mockedWebUtils.httpPostRequest(_, [text: message], _)
}
But code above is not working. Is there legal way?
Try something like:
void "test sending message"() {
given:
WebUtils.metaClass.static.httpPostRequest = { String url, Map data, Map headers ->
return 'done' // you can do what you want here, just returning a string as example
}
when:
service.sendMessage( 'Test' )
then:
1
// test for something your method has done
}
The correct way is using GroovyMock instead Mock:
void "test sending message"() {
given:
String text = 'Test'
GroovyMock(global:true, WebUtils)
when:
service.sendMessage(text)
then:
1*WebUtils.httpPostRequest(_, [text: text], _)
}
I've found here:http://spockframework.org/spock/docs/1.3-RC1/interaction_based_testing.html#_mocking_static_methods
Is it possible to cast in clojure with java style?
This is java code which I want to implement in clojure:
public class JavaSoundRecorder {
// the line from which audio data is captured
TargetDataLine line;
/**
* Captures the sound and record into a WAV file
*/
void start() {
try {
AudioFormat format = new AudioFormat(16000, 8,
2, true, true);
DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
System.out.println(AudioSystem.isLineSupported(info));
// checks if system supports the data line
if (!AudioSystem.isLineSupported(info)) {
System.out.println("Line not supported");
System.exit(0);
}
//line = (TargetDataLine) AudioSystem.getLine(info);
line = AudioSystem.getTargetDataLine(format);
line.open(format);
line.start(); // start capturing
System.out.println("Start capturing...");
AudioInputStream ais = new AudioInputStream(line);
System.out.println("Start recording...");
// start recording
AudioSystem.write(ais, AudioFileFormat.Type.WAVE, new File("RecordAudio.wav"));
} catch (LineUnavailableException ex) {
ex.printStackTrace();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
/**
* Closes the target data line to finish capturing and recording
*/
void finish() {
line.stop();
line.close();
System.out.println("Finished");
}
/**
* Entry to run the program
*/
public static void main(String[] args) {
final JavaSoundRecorder recorder = new JavaSoundRecorder();
// creates a new thread that waits for a specified
// of time before stopping
Thread stopper = new Thread(new Runnable() {
public void run() {
try {
Thread.sleep(6000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
recorder.finish();
}
});
stopper.start();
// start recording
recorder.start();
}
}
And this is what I made in clojure
(def audioformat (new javax.sound.sampled.AudioFormat 16000 8 2 true true))
(def info (new javax.sound.sampled.DataLine$Info javax.sound.sampled.TargetDataLine audioformat))
(if (not= (javax.sound.sampled.AudioSystem/isLineSupported info))(print "dataline not supported")(print "ok lets start\n"))
(def line (javax.sound.sampled.AudioSystem/getTargetDataLine audioformat))
(.open line audioformat)
are there any solutions?
this issue was explained rather well on the Clojure group here:
https://groups.google.com/forum/#!topic/clojure/SNcT6d-TTaQ
You should not need to do the cast (see the discussion in the comments about the super types of the object we have), however you will need to type hint the invocation of open:
(.open ^javax.sound.sampled.TargetDataLine line audioformat)
Remember that java casts don't really do very much (not like C++ where a cast might completely transform an underlying object).
I am not sure what this code is supposed to do, so I don't know whether it has worked or not. Certainly, I can now run your example without error.