I've set up a JMock context in my JUnit #Before test, something like:
context.checking(new Expectations()
{
{
allowing(mockPayload).getContent();
will(returnValue(FileUtils.readFileToByteArray(XML_FILE)));
}
});
This reads in an xml file and does some tests on it's content. Later on in the test I want to read in a different file and do similar tests. Is there anyway to do this without creating a new 'context', ie, by re-using the context to read in a different file. I know that Junit doesn't guarantee the order of the tests you carry out, so trying to reuse a context that might not have been used yet is going to cause an error. Any ideas?
Related
I have this method that I am using in a NetBeans plugin:
public static SourceCodeFile getCurrentlyOpenedFile() {
MainProjectManager mainProjectManager = new MainProjectManager();
Project openedProject = mainProjectManager.getMainProject();
/* Get Java file currently displaying in the IDE if there is an opened project */
if (openedProject != null) {
TopComponent activeTC = TopComponent.getRegistry().getActivated();
DataObject dataLookup = activeTC.getLookup().lookup(DataObject.class);
File file = FileUtil.toFile(dataLookup.getPrimaryFile()); // Currently opened file
// Check if the opened file is a Java file
if (FilenameUtils.getExtension(file.getAbsoluteFile().getAbsolutePath()).equalsIgnoreCase("java")) {
return new SourceCodeFile(file);
} else {
return null;
}
} else {
return null;
}
}
Basically, using NetBeans API, it detects the file currently opened by the user in the IDE. Then, it loads it and creates a SourceCodeFile object out of it.
Now I want to unit test this method using JUnit. The problem is that I don't know how to test it.
Since it doesn't receive any argument as parameter, I can't test how it behaves given wrong arguments. I also thought about trying to manipulate openedProject in order to test the method behaviour given some different values to that object, but as far as I'm concernet, I can't manipulate a variable in JUnit that way. I also cannot check what the method returns, because the unit test will always return null, since it doesn't detect any opened file in NetBeans.
So, my question is: how can I approach the unit testing of this method?
Well, your method does take parameters, "between the lines":
MainProjectManager mainProjectManager = new MainProjectManager();
Project openedProject = mainProjectManager.getMainProject();
basically fetches the object to work on.
So the first step would be to change that method signature, to:
public static SourceCodeFile getCurrentlyOpenedFile(Project project) {
...
Of course, that object isn't used, except for that null check. So the next level would be to have a distinct method like
SourceCodeFile lookup(DataObject dataLookup) {
In other words: your real problem is that you wrote hard-to-test code. The "default" answer is: you have to change your production code, to make easier to test.
For example by ripping it apart, and putting all the different aspects into smaller helper methods.
You see, that last method lookup(), that one takes a parameter, and now it becomes (somehow) possible to think up test cases for this. Probably you will have to use a mocking framework such as Mockito to pass mocked instances of that DataObject class within your test code.
Long story short: there are no detours here. You can't test your code (in reasonable ways) as it is currently structured. Re-structure your production code, then all your ideas about "when I pass X, then Y should happen" can work out.
Disclaimer: yes, theoretically, you could test the above code, by heavily relying on frameworks like PowerMock(ito) or JMockit. These frameworks allow you to contol (mock) calls to static methods, or to new(). So they would give you full control over everything in your method. But that would basically force your tests to know everything that is going on in the method under test. Which is a really bad thing.
I am trying to write some JUnit tests for a set of methods which use some REST services on the web.
In general, within my methods, I am providing a filepath and a configuration as a parameter, but I expect things will get more complicated as I progress.
For right now, what are the best ways for me to write JUnit tests for the likes of :
public Answers changeFileToAnswer(String filePath, String mediaType) {
File document = new File(filePath);
Answers answers = restService.changeFileToAnswer(document, mediaType);
return answers;
}
What kind of Unit tests can I write for a simple class like this? Testing the answers object would be an integration tests, since an external call is made here, right? What is good practise here? Is there a way to mock the filepath being passed in as a parameter?
Notes -
This method is from a REST interface which will later be exposed through a GUI. I am currently testing it with POST calls from POSTman. Due to this, I am passing in a string for the filePath rather than a file object (as I could not post this to my server).
Thanks.
The test is not necessary to be integration. Your restService need to be mock or fake, so there is no real external call.
For mocking filePath you can use JUnit TemporaryFolder.
public class TestClass{
#Rule
private TemporaryFolder folder = new TemporaryFolder();
#Test
public void testMethod(){
File tempFile = folder.newFile("myfile.txt");
classUnderTest.changeFileToAnswer(file.getPath(), mediaType);
}
}
This rule will create a real file in file system which will be removed when tests finish execution.
UPD: You might also want to take a look at jimfs
Java 8 here but this is a general unit testing question that (is likely) language-agnostic.
The syntax of writing a JUnit test is easy, but deciding on what tests to write and how to test main/production code is what I find to be the biggest challenge. In reading up on unit testing best practices, I keep hearing the same thing over and over again:
Test the contract
I believe the idea there is that unit tests should not be brittle and should not necessarily break if the method's implementation changes. That the method should define a contract of inputs -> results/outcomes and that the tests should aim to verify that contract is being honored. I think.
Let's say I have the following method:
public void doFizzOnBuzz(Buzz buzz, boolean isFoobaz) {
// wsClient is a REST client for a microservice
Widget widget = wsClient.getWidgetByBuzzId(buzz.getId());
if(widget.needsFile()) {
File file = readFileFromFileSystem(buzz.getFile());
if(isFoobaz) {
// Do something with the file (doesn't matter what)
}
}
return;
}
private File readFileFromFileSystem(String filename) {
// Private helper method; implementation doesn't matter here EXCEPT...
// Any checked exceptions that Java might throw (as a result of working)
// with the file system are wrapped in a RuntimeException (hence are now
// unchecked.
// Reads a file from the file system based on the filename/URI you specify
}
So here, we have a method we wish to write unit tests for (doFizzOnBuzz). This method:
Has two parameters, buzz and isFoobaz
Uses a class property wsClient to make a network/REST call
Calls a private helper method that not only works with the external file system, but that "swallows" checked exceptions; hence readFileFromFileSystem could throw RuntimeExceptions
What kinds of unit tests can we write for this that "test the contract"?
Validating inputs (buzz and isFoobaz) are obvious ones; the contract should define what valid values/states for each of those are, and what exceptions/results should occur if they are invalid.
But beyond that, I'm not really sure what the "contract" here would even be, which makes writing tests for it very difficult. So I guess this question really should be something like "How do I determine what the contract is for a unit test, and then how do you write tests that target the contract and not the implementation?"
But that title would be too long for a SO question.
Your code with the methods doFizzOnBuzz(Buzz buzz, boolean isFoobaz) and private File readFileFromFileSystem(String filename) is not easily testable, because the first method will try and read a file, and that's not something you want to do in test.
Here, doFizzOnBuzz needs something to provide a File for it to work with. This FileProvider (as I'll call it) could be an interface, something like:
public interface FileProvider {
File getFile(String filename);
}
When running in production, an implementation to actually read the file from disk is used, but when unit testing doFizzOnBuzz a mock implementation of FileProvider could be used instead. This returns a mock File.
The key point to remember is that when testing doFizzOnBuzz, we are not testing whatever provides the file, or anything else. We assume that to working correctly. These other bits of code have their own unit tests.
A mocking framework such as Mockito can be used a create mock implementations of FileProvider and File, and to inject the mock FileProvider into the class under test, probably using a setter:
public void setFileProvider(FileProvider f) {
this.fileProvider = f;
}
Also, I don't know what a wsClient is, bit I do know it has a getWidgetByBuzzId() method. This class too could be an interface, and for testing purposes the interface would be mocked, and return a mock Widget, similar to the FileProvider above.
With mockito, not only can you set up mock implementations of interfaces, you can also define what values are returned when methods are called on that interface: e.g.
//setup mock FileProvider
FileProvider fp = Mockito.mock(FileProvider.class);
//Setup mock File for FileProvider to return
File mockFile = Mockito.mock(File.class);
Mockito.when(mockFile.getName()).thenReturn("mockfilename");
//other methods...
//Make mock FileProvider return mock File
Mockito.when(fp.getFile("filename")).thenReturn(mockFile);
ClassUnderTest test = new ClassUnderTest();
test.setFileProvider(fp); //inject mock file provider
//Also set up mocks for Buzz,, Widget, and anything else
//run test
test.doFizzOnBuzz(...)
//verify that FileProvider.getFile() was actually called:
Mockito.verify(fp).getFile("filenane");
The above test fails if getFile() was not called with the parameter 'filename'
Conclusion
If you cannot directly observe the results of a method, e.g. it is void, you can use Mocking to verify its interaction with other classes and methods.
The problem is that your contract method does not tell what effect you can observe from the outside. It is basically a BiConsumer, so appart from ensuring there is an exception or not, there is not much unit testing possible.
The test you could do is to ensure that the (Mocked) REST service is called, or that the File (part of the Buzz parameter, which might be pointing to a temporary file) will be impacted by the method under some conditions.
If you want to unit test the output of the method, you may need to refactor to separate the determination of what should be done (file needs update) from actually doing it.
I have an existing Java class that writes to a file.
public final class WriteToFile{
private Writer file_writer;
private static final String encoding_format = "UTF8";
private FileWrite(final File fpath) throws IOException {
this.file_writer = new OutputStreamWriter(new FileOutputStream(fpath), encoding_format);
}
#Override
public void fileWrite(final String msg) {
try {
this.file_writer.write(msg);
this.file_writer.write("\n");
this.file_writer.flush();
this.file_writer.close();
} catch (IOException e) {
log.error("File write failed", e);
}
}
}
In order to unit test this, I learnt that creating a file mock using a Mocking framework is not a good practice . What do I test here? The only way of testing this is to probably do the file write again, and check if the expected contents and actual contents are the same. In that case, doing it the JUnit way would be as mentioned in this post How to test write to file in Java?. However, I am not going to rewrite the file writing code, to include interface wrappers. How do I go about with this?
#Test public void testfileWrite() {
String msg = "somemessage";
String fpath = "path/to/file";
Writer file_writer = new OutputStreamWriter(new FileOutputStream(fpath), "UTF8");
file_writer.write(msg);
assertEquals("somemessage", file_writer.toString());
}
Is this all that needs to be tested?
The point of this class is to write a file. It does nothing else (and that is a good thing). So don't bother with a mockist unit test, all it shows is that you can write a ton of mock code. Instead write a Integration Test.
Use the JUnit rule TemporaryFolder to create and destroy a folder to put your test file in, then verify the file has what you want in it at the end of the test. The only time you should consider mocking for this kind of test is if the exceptional case does something funky. Then you can either do some evil black magic involving Powermock or pass in some form of "File Stream factory". Or ask yourself if that is really such a great place for complex logic that needs testing, and then move it.
When testing classes that make use of WriteToFile, mock or stub WriteToFile.
When it comes to write unit tests that must check generated files, I always prepare myself a repository of cases: For each case, an input file (if necessary), and and a set of expected output files.
I write one test method for each case, where I call the business logic, which will generate one (or some) file into the working directory, and I eventually check if the generated file is equal to the proper expected file.
I prepare the expected files manually and check them in into the Source Control System, so that they belong to each released version. If, in future, the business logic must change its behaviour, then it is required that the expected file be changed accordingly, and that both the code and the file be checked in and tagged together in the same release.
That is the easiest and safest way I found for checking generated files.
Use Powermockito to mock the call to the constructor of FileOutputStream and OutputStreamWriter as in: http://benkiefer.com/blog/2013/04/23/powermockito-constructor-mocking/
Then verify that file_writer methods write(String) and flush() have been invoked twice and once, respectively; and at the end close().
I am trying to speed up the Integration tests in our environment. All our classes are autowired. In our applicationContext.xml file we have defined the following:
<context:annotation-config/>
<context:component-scan base-package="com.mycompany.framework"/>
<context:component-scan base-package="com.mycompany.service"/>
...additional directories
I have noticed that Spring is scanning all directories indicated above and then iterates over each bean and caches the properties of each one. (I went over the DEBUG messages from spring)
As a result, the following test takes about 14 seconds to run:
public class MyTest extends BaseSpringTest {
#Test
def void myTest(){
println "test"
}
}
Is there any way to lazy load the configuration? I tried adding default-lazy-init="true" but that didn't work.
Ideally, only the beans required for the test are instantiated.
thanks in advance.
Update: I should have stated this before, I do not want to have a context file for each test. I also do not think one context file for just the tests would work. (This test context file would end up including everything)
If you really want to speed up your application context, disable your <component-scan and performs the following routine before running any test
Resource resource = new ClassPathResource(<PUT_XML_PATH_RIGHT_HERE>); // source.xml, for instance
InputStream in = resource.getInputStream();
Document document = new SAXReader().read(in);
Element root = document.getRootElement();
/**
* remove component-scanning
*/
for ( Iterator i = root.elementIterator(); i.hasNext(); ) {
Element element = (Element) i.next();
if(element.getNamespacePrefix().equals("context") && element.getName().equals("component-scan"))
root.remove(element);
}
in.close();
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(true);
for (String source: new String[] {"com.mycompany.framework", "com.mycompany.service"}) {
for (BeanDefinition bd: scanner.findCandidateComponents(source)) {
root
.addElement("bean")
.addAttribute("class", bd.getBeanClassName());
}
}
//add attribute default-lazy-init = true
root.addAttribute("default-lazy-init","true");
/**
* creates a new xml file which will be used for testing
*/
XMLWriter output = new XMLWriter(new FileWriter(<SET_UP_DESTINATION_RIGHT_HERE>));
output.write(document);
output.close();
Besides that, enable <context:annotation-config/>
As you need to perform the routine above before running any test, you can create an abstract class where you can run the following
Set up a Java system property for testing environment as follows
-Doptimized-application-context=false
And
public abstract class Initializer {
#BeforeClass
public static void setUpOptimizedApplicationContextFile() {
if(System.getProperty("optimized-application-context").equals("false")) {
// do as shown above
// and
System.setProperty("optimized-application-context", "true");
}
}
}
Now, for each test class, just extends Initializer
One approach is to skip the auto detection completely and either load up a separate context (with the components required for the test) or redefine your beans at runtime (prior to the test running).
This thread discusses redefinition of beans and a custom test class for doing this:
Spring beans redefinition in unit test environment
This is the price you pay for auto-detection of components - it's slower. Even though your test only requires certain beans, your <context:component-scan> is much broader, and Spring will instantiate and initialise every bean it finds.
I suggest that you use a different beans file for your tests, one which only defines the beans necessary for the test itself, i.e. not using <context:component-scan>.
Probably what you need is to refactor your config to use less autowiring. My approach is almost always wire the beans by name, trying to be explicit with the design but, at the same time, not being too verbose either, using autowiring when is clear that you are using it in order to hide minor details.
Addendum:
If that is not enough and you are using junit, you may want to use a utility from the JUnit Addons project. The class DirectorySuiteBuilder dynamically builds up a test suite from a directory structure. So you can make something like
DirectorySuiteBuilder builder = new DirectorySuiteBuilder();
Test suite = builder.suite("project/tests");
Initializing the Spring context before this code, you can run all tests at once. However, if each test assume a "clean" Spring context, then you are probably lost.
In this kind of situation, you will need to find a balance.
On one hand, you would rightly want to run the tests in a shortest possible time to get the results quick. This is especially important when working in a team environment with continuous integration working.
On the other hand, you would also rightly want to keep the configuration of tests as simple as possible so the maintenance of test suite would not become too cumbersome to be useful.
But at the end of the day, you will need to find your own balance and make a decision.
I would recommend creating a few context configuration files for testing to group some tests so such a simple test would not take long time simply being configured by Spring, while keeping the number of configuration files to minimum you can manage.
Convention bean factory is designed to solve this problem and speeds up the whole process significantly, 3x or more.
Since none of the answers here solved this problem for me, I add my own experience.
My problem was that Spring, Hibernate and EhCache grouped up in the attempt of drowning my console with verbose DEBUG messages, resulting unreadable log and - far worse - unbearable low performance.
Configuring their log levels fixed all up:
Logger.getLogger("org.hibernate").setLevel(Level.INFO);
Logger.getLogger("net.sf.ehcache").setLevel(Level.INFO);
Logger.getLogger("org.springframework").setLevel(Level.INFO);