skip methods from tested class when using JUnit - java

I have the following situation. I have a class
A {
public void setProps() {
// setting propertis
makeSomeWeirdConfigs();
//setting another properties
}
private void makeSomeWeirdConfigs() {
// making configs
}
public Props getProps() {
//getting properties
}
}
Now I want to check with the help of JUnit test case if the properties are properly set by using method setProps() and then getting the properties with getProps().
The problem is this method makeSomeWeirdConfigs() in the middle of the setProps(), which when executed without the proper environment cause a lot of exceptions, so the props below this method are not set.
Also this method tries to do dangerous things for example executing some scripts etc. So my question is is it possible to skip this method makeSomeWeirdConfigs() somehow in the JUnit test case, so only setProps() and getProps() are executed.
The original class A should not be changed.
Thanks

"Original class should not be changed" - then you are seeking something mocking, byte code fiddling, AOP.
If you made makeSomeWeirdConfig protected, you could override the method and test a child class. The same with testing an extra flag.
If you could put the code in a delegator even better towards sound code.
class SomeWeirdConfig {
void makeSomeWeirdConfigs() { ... }
}
SomeWeirdConfig delegate = new SomeWeirdConfig();
public void initTest() { delegate = null; }
private void makeSomeWeirdConfigs() {
if (delegate != null) {
delegate.makeSomeWeirdConfigs();
}
}
Then the test could disable the setter.

Related

best practice: how to set config file at runtime for tests

As a homework exercise (I'm a beginner) I had to write a Java program which accesses a database (PostgreSQL). The program can, e.g., insert new users, increment some fields etc. and I have methods such as: addUser(User t), deleteUser(User t) and so on.
I also have written test methods using junit5. For the tests I use a 'test' database, separate from the 'work' one. The coordinates to open the two databases (database name, password etc.) are stored in two files called config.properties and config.test.properties, which are selected at runtime.
What I have in place now is something along these lines, using a boolean flag variable:
public class UserDao {
public boolean isTestMode = false;
public Connection getConnection() {
if (this.isTestMode) {
input = cl.getResourceAsStream("config.test.properties");
} else {
input = cl.getResourceAsStream("config.properties");
}
...
}
}
In my test methods I then set the flag like this:
void testAddUser() {
UserDao dao = new UserDao();
dao.isTestMode = true;
...
}
In regular, non-test method I don't set isTestMode, which therefore stays to its default (false) value and config.properties is used.
My approach works, but my instructor told me it is bad practice to do things like this and I should change it, for example (he suggested) doing a dependency injection. I'm not too sure how to proceed.
I could make configFilename a class variable and add to the class UserDao a new constructor which accepts a filename, like this:
public class UserDao {
private String configFilename = "config.properties";
public UserDao() {
}
public UserDao(String filename) {
this();
this.configFilename = filename;
}
public Connection getConnection() {
input = cl.getResourceAsStream(this.configFilename);
...
}
}
Then in the test methods I use the new construtor UserDao("config.test.properties")
A (in my view a better) variant is to introduce a constructor which accepts a boolean isTestMode and sets configFilename accordingly (I don't need nor want the flexibility of specifying any filename in the constructor). But essentially this is the same as my original approach, which I was told to change. Also, there no dependency injection there... what is it best practice in such cases? Any suggestion would be welcome!
Passing parameter can be seen as a trivial dependency injection.
Concerning your java: when you set a default value for configFilename, you can see that as a convention that you use in your application.
Your class does not need that. And if you avoid this convention, you got immutability for free. For example, you can do :
public class UserDao {
private final String configFilename;
public UserDao(String filename) {
this.configFilename = filename;
}
public Connection getConnection() {
input = cl.getResourceAsStream(this.configFilename);
...
}
}
UserDao can be used by your test classes or your main classes the same way.
With your solution, you may deliver code that will never run in production (the branch where isTestMode is true), and that's not a good practice. This code could be seen as dead code in production.
In application use default constructor when creating an instance of UserDao, in junit pass name of file:
new UserDao("config.test.properties");
private final String configFilename;
public UserDao() {
this("config.properties");
}
public UserDao(String filename) {
this.configFilename = filename;
}

Ignore JUnit tests if variable value is null

I have a JUnit test class which runs 15 tests. 5 of the tests are optional in that I only wish to run them if a particular variable gets initialized by an argument. If the variable value is null I'd like to ignore these tests. Is this possible and if so, how?
You could use JUnit4's Assume feature ...
It's good to be able to run a test against the code as it is currently written, implicit assumptions and all, or to write a test that exposes a known bug. For these situations, JUnit now includes the ability to express "assumptions"
For example:
#Before
public void setUp() {
org.junit.Assume.assumeTrue(yourCondition());
// ...
}
If yourCondition() does not return true then the test for which #Before is running will not be executed.
Approach 1:
You can use JUnit-ext. It has RunIf annotation that performs conditional tests, like:
#Test
#RunIf(DatabaseIsConnected.class)
public void calculateTotalSalary() {
//your code there
}
class DatabaseIsConnected implements Checker {
public boolean satisify() {
return Database.connect() != null;
}
}
Approach 2
Another approach is to use Assume. You can do it in a #Before method or in the test itself, but not in an #After method. If you do it in the test itself, your #Before method will get run. You can also do it within #BeforeClass to prevent class initialization. For example:
#Before
public void beforeMethod() {
org.junit.Assume.assumeTrue(someCondition());
}
Approach 3
I think an another option for you may be to create an annotation to denote that the test needs to meet your custom criteria, then extend the default runner with your own and using reflection, base your decision on the custom criteria. It may look something like this:
public class CustomRunner extends BlockJUnit4ClassRunner {
public CTRunner(Class<?> klass) throws initializationError {
super(klass);
}
#Override
protected boolean isIgnored(FrameworkMethod child) {
if(shouldIgnore()) {
return true;
}
return super.isIgnored(child);
}
private boolean shouldIgnore(class) {
/* some custom criteria */
}
}

JMockit: Overriding #Mocked class

I have an internal StreamGobbler class that has 7 methods in it.
I'm looking for a quick way to mock all the methods by default, but override one method named getOutput() (e.g. Partial Mocking).
(full code not shown for clarity)
public class StreamGobbler extends Thread
{
public String getOutput()
public void run()
}
What I would like is to use something like the #Mocked annotation in combination with MockUp to partially mock the getOutput method, but retain all the "default" mocking code on all the other methods. In the docs on partial mocking, it makes the point that if you use MockUp, all non #Mock methods retain their normal functionality. Sometimes that is great, but that isn't what I want in this case.
This is similar to the question JMockit: #Mocke and MockUp combination in the same test, but I can't get away with just looking at method counts.
If I have a test setup like this:
#Test
public void execute(#Mocked StreamGobbler sg)
{
new MockUp<StreamGobbler>()
{
String type = null;
#Mock
void $init(String type)
{
this.type = type;
}
#Mock
String getOutput()
{
if ("OUTPUT".equals(type))
{
return "test output";
}
else
{
return "";
}
}
}
}
I get this error java.lang.IllegalArgumentException: Class already mocked
If I try to add the #Override annotation in the MockUp, it doesn't help (and Eclipse complains about it)
What is the best way to handle this? Use a static class outside this test method?
Using JMockit 1.17, and TestNG
In summary, how do I get every method in StreamGobbler mocked (as with #Mocked), but partially override one method (without manually doing it myself inside the MockUp?)
Full example code which meets the given constraints:
public static class StreamGobbler extends Thread {
public StreamGobbler(String type) {}
public String getOutput() { return null; }
#Override public void run() {}
}
public static class TestedClass {
public String doSomething() throws InterruptedException {
StreamGobbler sg1 = new StreamGobbler("OUTPUT");
sg1.start();
StreamGobbler sg2 = new StreamGobbler("ERROR");
sg2.start();
sg1.join(5000);
sg2.join(5000);
String output1 = sg1.getOutput();
String output2 = sg2.getOutput();
return output1 + '|' + output2;
}
}
#Test
public void useStreamGobbler(#Mocked StreamGobbler sg) throws Exception {
new Expectations() {{
new StreamGobbler("OUTPUT").getOutput(); result = "test output";
new StreamGobbler("ERROR").getOutput(); result = "";
}};
String output = new TestedClass().doSomething();
assertEquals("test output|", output);
}
Firstly, since you are creating an anonymous subclass of the MockUp class, using the #Override annotation would certainly be inappropriate. Those methods that you are providing do not belong to the MockUp class, but the generic you are providing.
Later on during runtime, (through some impressive process (based on what I read here, I'm assuming AOP)) the instance you create in this class will then use your provided method signatures instead of its own.
After reading the API on the Mock class more thoroughly as well as getting some information from JMockit's Getting Started page, I think you're issue lies in a different area entirely. If you have other test methods, they will be interfering with this method.
The error you are getting is saying: "There is already an instance of MockUp declared for the type StreamGobbler, and by calling the Mocked annotation in this test method's parameters and attempting to declare another instance of MockUp with the same generic, you are violating a JMockit stipulation."
I would check to see if you are creating an actual MockUp of StreamGobbler outside of the test method and if so (1) if you want to use it, don't redeclare another instance of MockUp in the method but continue to use the Mocked annotation or (2) if you do not want to use it and you want to re-declare a new instance of MockUp wrapping StreamGobbler, do not use the Mocked annotation in the test method's parameters but keep the MockUp instantiation.

Is this junit test even necessary?

My testing experience is mostly with Ruby's Rspec. And I recently tried to write a Java jUnit test. And here is an example. Then I started to wonder if this test is even necessary. Since properties() will either return a Properties or null - based on the method signature. So if I want to test the method, all I should do is to test if the return value is not null.
public class HelperTest {
#Test
public void testProperties() {
assertThat(Helper.properties(), instanceOf(Properties.class));
}
}
public class Helper {
public static Properties properties() {
// ... some code to init properties ...
return properties;
}
}
Thanks all for your comments. Let me try to answer my question based on the comments.
This is the new test:
public void testProperties() {
assertThat(Helper.properties(), is(notNullValue()));
}
I will simply test to make sure the method is not returning a null value. If the method is returning a non-null value, it will be an instance of a Properties. Which means it is unnecessary to test if the method returns an instance of a Properties.

Conditionally ignoring tests in JUnit 4

OK, so the #Ignore annotation is good for marking that a test case shouldn't be run.
However, sometimes I want to ignore a test based on runtime information. An example might be if I have a concurrency test that needs to be run on a machine with a certain number of cores. If this test were run on a uniprocessor machine, I don't think it would be correct to just pass the test (since it hasn't been run), and it certainly wouldn't be right to fail the test and break the build.
So I want to be able to ignore tests at runtime, as this seems like the right outcome (since the test framework will allow the build to pass but record that the tests weren't run). I'm fairly sure that the annotation won't give me this flexibility, and suspect that I'll need to manually create the test suite for the class in question. However, the documentation doesn't mention anything about this and looking through the API it's also not clear how this would be done programmatically (i.e. how do I programatically create an instance of Test or similar that is equivalent to that created by the #Ignore annotation?).
If anyone has done something similar in the past, or has a bright idea of how else I could go about this, I'd be happy to hear about it.
The JUnit way is to do this at run-time is org.junit.Assume.
#Before
public void beforeMethod() {
org.junit.Assume.assumeTrue(someCondition());
// rest of setup.
}
You can do it in a #Before method or in the test itself, but not in an #After method. If you do it in the test itself, your #Before method will get run. You can also do it within #BeforeClass to prevent class initialization.
An assumption failure causes the test to be ignored.
Edit: To compare with the #RunIf annotation from junit-ext, their sample code would look like this:
#Test
public void calculateTotalSalary() {
assumeThat(Database.connect(), is(notNull()));
//test code below.
}
Not to mention that it is much easier to capture and use the connection from the Database.connect() method this way.
You should checkout Junit-ext project. They have RunIf annotation that performs conditional tests, like:
#Test
#RunIf(DatabaseIsConnected.class)
public void calculateTotalSalary() {
//your code there
}
class DatabaseIsConnected implements Checker {
public boolean satisify() {
return Database.connect() != null;
}
}
[Code sample taken from their tutorial]
In JUnit 4, another option for you may be to create an annotation to denote that the test needs to meet your custom criteria, then extend the default runner with your own and using reflection, base your decision on the custom criteria. It may look something like this:
public class CustomRunner extends BlockJUnit4ClassRunner {
public CTRunner(Class<?> klass) throws initializationError {
super(klass);
}
#Override
protected boolean isIgnored(FrameworkMethod child) {
if(shouldIgnore()) {
return true;
}
return super.isIgnored(child);
}
private boolean shouldIgnore(class) {
/* some custom criteria */
}
}
Additionally to the answer of #tkruse and #Yishai:
I do this way to conditionally skip test methods especially for Parameterized tests, if a test method should only run for some test data records.
public class MyTest {
// get current test method
#Rule public TestName testName = new TestName();
#Before
public void setUp() {
org.junit.Assume.assumeTrue(new Function<String, Boolean>() {
#Override
public Boolean apply(String testMethod) {
if (testMethod.startsWith("testMyMethod")) {
return <some condition>;
}
return true;
}
}.apply(testName.getMethodName()));
... continue setup ...
}
}
A quick note: Assume.assumeTrue(condition) ignores rest of the steps but passes the test.
To fail the test, use org.junit.Assert.fail() inside the conditional statement. Works same like Assume.assumeTrue() but fails the test.

Categories

Resources