I know there are several question about void-method Unit-Testing, but my question is different.
I'm learning java, so my boss give me some tasks with different requirements on my tasks.
In my actual task, there is a requirement which says, the jUnit test must cover >60%. So I need to test a very simple method to reach this 60%. The method is the following:
public void updateGreen() {
// delete this outprint if the Power Manager works
System.out.println(onCommand + "-green");
// p = Runtime.getRuntime().exec(command + "-green");
// wait until the command is finished
// p.waitFor();
}
Because of intern problems, I can't execute the command with the Runtime task. So there is only a System.out in this method.
I've multiple methods like that, so tests for this method will cover over 10% of my whole code.
Is it useful to test such a method? When yes, how?
If there is a lot of such methods, the thing which you might want to test here is that updateScreen() uses the right string, "some-command-green" and that the System.out is being invoked. In order to do this you might want to extract System.out into an object field and mock it (i.e. with Mockito's spy()) to test the string that was provided to println.
I.e.
class MyClass{
PrintStream out = System.out;
public void updateGreen() { ... }
}
In test:
#Test
public void testUpdate(){
MyClass myClass = new MyClass();
myClass.out = Mockito.spy(new PrintStream(...));
// mock a call with an expected input
doNothing().when(myClass.out).println("expected command");
myClass.updateGreen();
// test that there was a call
Mockito.verify(myClass.out, Mockito.times(1)).println("expected command");
}
You could return true if the method ran successfully and false otherwise. It would be easy to test for this.
You could also test the output of this method, as described here:
Should we unit test console outputs?
But in my experience, it is much better to have methods return an optimistic or pessimistic value (true/false, 1/0/-1 etc) to indicate their status.
You can also write a getter method for the onCommand flag:
public string getFlag(){
// some logic here
return "green";
// otherwise default to no flags
return "";
}
You could test that onCommand + "-green" has been written to System.out by using the System Rules library.
Related
I am somewhat new to test driven development and am trying to determine whether I have a problem with my approach to unit testing.
I have several unit tests (let's call them group A) that test whether my method's return is as expected.
I also have a unit test "B" whose passing condition is that an IllegalArgumentException is thrown when my method is given invalid input.
The unit tests in group A fail when the method is given invalid input since the method needs valid input to return correctly.
If I catch the exception, unit test "B" will fail, but if I don't catch the exception, the tests in group A will fail.
Is it OK to have unit tests fail in this way, or can I modify the code in some way so that all tests always pass?
Am I doing TDD all wrong?
Here's a notion of my code for more clarity:
public class Example{
public static String method(String inputString, int value){
if(badInput){
throw new IllegalArgumentException();
}
//do some things to inputString
return modifiedInputString;
}
}
public class ExampleTests{
#Test
public void methodReturnsIllegalArgumentExceptionForBadInput(){
assertThrows(IllegalArgumentException.class, ()->{Example.method(badInput,badValue);})
}
//"Group A" tests only pass with valid input. Bad input causes IllegalArgumentException
#Test
public void methodReturnsExpectedType(){
assertTrue(actual == expected);
}
#Test
public void methodReturnsExpectedValue(){
assertTrue(actual == expected);
}
#Test
public void methodReturnsExpectedStringFormat(){
assertTrue(actual == expected);
}
}
As you've correctly noted in the comment the problem is with too broad test setup and too implicit tests.
Tests are much more readable when they are self-contained on the business level. Common test setup should be focused on setting up technical details, but all business setup should be within each test itself.
Example for your case (a conceptual example, must be reworked to match the details of your implementation):
#Test
public void givenWhatever_whenDoingSomething_methodReturnsExpectedType(){
given(someInputs);
Type result = executeSut(); //rename executeSut to actual function under test name
assertTrue(result == expected);
}
This way by just looking at a test a reader knows what scenario is being tested. Common test setup and helper functions such as given abstract away technical details, so the reader does not get distracted at the first inspection. If they are interested, the details are always available - but are usually less important, such can be hidden.
i am using eclemma and trying to increase my test coverage:
so far this is my code:
public RolesResponse findRolesByTenant(RolesRequest rolesRequest)
{
RolesResponse rolesResponse = new RolesResponse();
List<Role> roleList = null;
if (StringUtils.isNotBlank(rolesRequest.getTenantCode()))
{
roleList = roleFunctionService.getAllRolesAndFunctionsByTenant(rolesRequest.getTenantCode());
}
if (CollectionUtils.isNotEmpty(roleList))
{
rolesResponse.setRoles(roleList);
}
else
{
rolesResponse.setError(LayerContextHolder.getErrorObject());
}
return rolesResponse;
}
and here is my test:
#Test
public void findRolesByTenantTest()
{
RolesRequest rolesRequest = new RolesRequest();
rolesRequest.setTenantCode("test");
ErrorObject errorObject = new ErrorObject();
RolesResponse rolesResponse = rolesProcessService.findRolesByTenant(rolesRequest);
Assert.assertNull(rolesResponse.getError());
}
the only line eclemma is highlighting in red is this one:
rolesResponse.setError(LayerContextHolder.getErrorObject());
can someone help me in constructing the final test needed to cover this line
thanks
I'm really not a fan of your test anyway - what are you trying to prove by the error being null? That the list came back with something? Also, are you certain that your service will return the result you want in your test every single time?
Don't think of tests in terms of coverage; this will lead to brittle tests and tests that give a false sense of security. What you want to do is write tests that cover each condition that the code could encounter, and the line coverage can follow from that.
From your code, I see two cases.
roleFunctionService#getAllRolesByFunctionAndTenant can return a non-empty list.
It's implied that the resultant rolesResponse#roles contains whatever was in the list provided by the method, and this should be verified.
It's also implied that there is no error set on the object, so it should be null.
roleFunctionService#getAllRolesByFunctionAndTenant can return an empty list
Either the resultant rolesResponse#roles are empty or null; it'd be better if it were empty.
It's implied that there is an error on the object, which is specifically provided by LayerContextHolder.getErrorObject(). You should check to see that it's exactly that.
You'll get to the whole approach of writing this test through the use of a mocking framework.
For testing purpose, I try to "fake" some objects. I want to do the following: I have an object, and want to add new methods, or overwrite some. Sadly, unlike in Java, its not possible to create "nameless" classes. Ok, I could do it by simply creating a new class, but I want to do it dinamicaly.
This is the class:
class Test
{
public function method1()
{
return 'oldmethod1';
}
public function method2()
{
return 'oldmethod2';
}
public static function staticmethod1()
{
return 'staticmethod1';
}
public static function staticmethod2()
{
return 'staticmethod2';
}
}
and now what I want to do:
$a = new Test();
$b = new CreateMockObjectFromObject($a);
$b->newmethod = function() { return 'newmethod'; };
$b->method2 = function() { return 'method2 is overwritten'; };
$b->staticmethod2 = function() { return 'staticmethod2 overwritten'; };
echo $b->method1().'<br>';
echo $b->method2().'<br>';
echo $b::staticmethod1().'<br>';
echo $b::staticmethod2().'<br>';
Here you can see my wishes: call a normal method, overwrite a method, call a static method, overwrite a method. The results: FAIL, SUCCESS, FAIL, FAIL.
I have a helper class:
class CreateMockObjectFromObject
{
private $sourceObj;
/**
* #return
*/
public function __call ($method, $args)
{
if (isset($this->sourceObj->$method))
{
return call_user_func_array($this->sourceObj->$method, $args);
}
if (isset($this->$method))
{
return call_user_func_array($this->$method, $args);
}
throw new Exception ($method.' NOT FOUND');
}
/**
* #return
*/
public static function __callStatic ($method, $args)
{
// I cant even imagine this...
}
/**
* #return CreateMockObjectFromObject
*/
public function __construct ($sourceObj)
{
$this->sourceObj = $sourceObj;
}
}
I cant even imagine what about static methods. How to write this helper class so that all mocking/faking can work? And I didnt even talk about "const"-s...
once again, I know it all could be done with extending, but I need to do in this way!
Mocking an entire class:
$mock = Mockery::mock('FQ\ClassName');
Mocking only certain methods:
$mock = Mockery::mock('FQ\ClassName[method1, method2]')
Here, method3, method4, ..., methodN will function exactly as they do in your class implementation.
Making an expectation:
$mock->shouldReceive('method1')
->once()
->andReturn('a value')
;
After you make an expectation, then you Act upon the system under test:
$return = $objectImTesting->performAction($mock);
Once you act, you should assert that any return value is what it should be, given the input you provided:
$this->assertEquals('a value', $return);
Taking the above example, the test will fail if any of the following conditions are true:
method1 on your mock object is never called
method1 on your mock object is called more than once
the return value of performAction is not the literal value 'a value'
Now, you've not reinvented the wheel and instead already gotten started writing tests. Also for free, you get everything that PHPUnit and Mockery provide you (how long do you think it would take you by yourself to be able to support Demeter Chains in your mocking framework?).
Don't get me wrong, by all means go ahead and develop your testing framework if all you're interested in is learning. However, in my opinion I would not want to trust a testing framework that I developed by myself to ensure the code that I write works. I'd much rather use something that's open source and has been around for awhile so that I can sleep easier at night.
More information:
PHPUnit Documentation
Mockery Documentation
I have a void method and I want to test it. How do I do that?
Here's the method:
public void updateCustomerTagCount() {
List<String> fileList = ImportTagJob.fetchData();
try {
for (String tag : fileList) {
Long tagNo = Long.parseLong(tag);
Customer customer = DatabaseInterface.getCustomer(tagNo);
customer.incrementNoOfTimesRecycled();
DatabaseInterface.UpdateCustomer(customer);
}
} catch(IllegalArgumentException ex) {
ex.printStackTrace();
}
}
when the method returns void, you can't test the method output. Instead, you must test what are the expected consequences of that method. For example:
public class Echo {
String x;
public static void main(String[] args){
testVoidMethod();
}
private static void testVoidMethod() {
Echo e = new Echo();
//x == null
e.voidMethod("xyz");
System.out.println("xyz".equals(e.x)); //true expected
}
private void voidMethod(String s) {
x = s;
}
}
It might not be always true, but basic concept of unit test is to check if function works as expected and properly handling errors when unexpected parameters/situation is given.
So basically unit test is against the functions that takes input parameters and return some output so we can write those unit test.
The code like yours, however, includes some other dependency (database call) and that's something you can't execute unless you write integration-test code or real database connection related one and actually that's not recommended for unit test.
So what you need to do might be introducing unit test framework, especially Mockto/Powermock or some other stuff that provides object mocking feature. With those test framework, you can simulate database operation or other function call that is going to be happening outside of your test unit code.
Also, about how do I test void function, there is nothing you can with Assert feature to compare output since it returns nothing as you mentioned.
But still, there is a way for unit test.
Just call updateCustomerTagCount() to make sure function works. Even with just calling the function, those unit test can raise your unit test coverage.
Of course for your case, you need to mock
ImportTagJob.fetchData();
and
DatabaseInterface.getCustomer(tagNo);
and have to.
Let mocked
ImportTagJob.fetchData();
throw empty list as well as non-empty list and check if your code works as you expected. Add exception handling if necessary. In your code, there are two condition depends on whether fieList are null or non-null, you need to test it.
Also, mock those objects and let them throw IllegalArgumentException where you expect it to be thrown, and write an unit test if the function throws a exception. In Junit, it should be like
#Test(expected = IllegalArgumentException.class)
public void updateCustomerTagCountTest(){
// mock the objects
xxxxx.updateCustomerTagCount();
}
That way, you can ensure that function will throw exception properly when it has to.
So I'm tesing using Junit, quite new to it.
I am trying to test methods in a class called SetOfUsers as follows:
#Test
public void testFindUserByName() {
System.out.println("findUserByName");
String name = "";
SetOfUsers instance = new SetOfUsers();
User expResult = null;
User result = instance.findUserByName(name);
assertEquals(expResult, result);
// TODO review the generated test code and remove the default call to fail.
}
So I wanted to check the name of a user entered in Bob for instance in the name string like this
String name = "Bob";
since I have a user called Bob in the setOfUsers class.
The output window displays this message
Failed: expected:<null> but was:<Staff name:Bob, Staff pass:abc123>
What can I do to make this a pass?
Read about BDD, this is very nice technique for making tests easy to write and understand (read)
Test-driven development is a software development methodology which essentially states that for each unit of software, a software developer must:
define a test set for the unit first;
then implement the unit;
finally verify that the implementation of the unit makes the tests succeed.
Well written test should have GivenWhenThen sections
(Given) some context
(When) some action is carried out
(Then) a particular set of observable consequences should obtain
This style is known as SpecificationByExample
Given-When-Then is a style of representing tests - or as its advocates would say - specifying a system's behavior using SpecificationByExample.
Example test
#Test
public void testFindUserByName() {
// given
SetOfUsers instance = new SetOfUsers();
// when
User result = instance.findUserByName("Bob");
// then
assertEquals("Bob", result.getName());
}
Nice to read:
Arrange Act Assert Alternatives
Maintainable Tests
This test is always going to fail because the last line is
fail("The test case is a prototype.");
The reason your test is failing now is because of the line above,
assertEquals(expResult, result);
You are setting your expected result to null and the result you are getting from the name, "", is probably an empty String as well from that error message. You need to have expResult to be the same as what you expect instance.findUserByName("Bob") to return. However, unless you initialize the instance to be set with a User Object the objects will not match, so it might be better to either mock it to return a pre-created User object so they match, or create a User object with the same properties as the one you expect to be returned check the fields of the User Object returned and the User object you created to be sure they match.
If you want to check for what the user for Bob is, change the code to this:
#Test
public void testFindUserByName() {
System.out.println("findUserByName");
String name = "Bob";
SetOfUsers instance = new SetOfUsers();
User expResult = <Create an object you expect instance.findUserByName("Bob") to return>;
User result = instance.findUserByName(name);
//Check fields here.
assertEquals(expResult.getUserName(), result,getUserName());
// TODO review the generated test code and remove the default call to fail.
}
You can't test for null using assertEquals().
To test for null, use:
assertNull(result);
I don't understand the question, but if you want to search for "Bob" why you initialize name=""? The test should be:
#Test
public void testFindUserByName() {
//Create SetOfUsers
//Add new User with name Bob
//FindByUsername("Bob")
//AssertEqual(User.getName(), "Bob")
}