Spring boot Unit test about best practics and currectli writing tests - java

I want start write unit test in my project. I tried make this many times. And he always quit, because he could not understand the meaning. because I can not find and form knowledge into a single whole. I read many articles, saw many examples, and they are all different. As a result, I understand why I need write tests, I understand how to write them, but I do not understand how correctly. And I do not understand how to write them so that they are useful. I have some questions:
For example I have service:
#Service
public class HumanServiceImpl implements HumanService {
private final HumanRepository humanRepository;
#Autowired
public HumanServiceImpl(HumanRepository humanRepository) {
this.humanRepository = humanRepository;
}
#Override
public Human getOneHumanById(Long id) {
return humanRepository.getOne(id);
}
#Override
public Human getOneHumanByName(String firstName) {
return humanRepository.findOneByFirstName(firstName);
}
#Override
public Human getOneHumanRandom() {
Human human = new Human();
human.setId(Long.parseLong(String.valueOf(new Random(100))));
human.setFirstName("firstName"+ System.currentTimeMillis());
human.setLastName("LastName"+ System.currentTimeMillis());
human.setAge(12);//any logic for create Human
return human;
}
}
And I tried write Unit test for this service:
#RunWith(SpringRunner.class)
public class HumanServiceImplTest {
#MockBean(name="mokHumanRepository")
private HumanRepository humanRepository;
#MockBean(name = "mockHumanService")
private HumanService humanService;
#Before
public void setup() {
Human human = new Human();
human.setId(1L);
human.setFirstName("Bill");
human.setLastName("Gates");
human.setAge(50);
when(humanRepository.getOne(1L)).thenReturn(human);
when(humanRepository.findOneByFirstName("Bill")).thenReturn(human);
}
#Test
public void getOneHumanById() {
Human found = humanService.getOneHumanById(1L);
assertThat(found.getFirstName()).isEqualTo("Bill");
}
#Test
public void getOneHumanByName() {
Human found = humanService.getOneHumanByName("Bill");
assertThat(found.getFirstName()).isEqualTo("Bill");
}
#Test
public void getOneHumanRandom() {
//???
}
}
I have questions:
1. Where should I fill the objects? I saw different implementations
in #Before like in my example, in #Test, mix implementations - when Human create in #Before and expression
when(humanRepository.getOne(1L)).thenReturn(human);
in #Test method
private Human human;
#Before
public void setup() {
human = new Human();
...
}
#Test
public void getOneHumanById() {
when(humanRepository.getOne(1L)).thenReturn(human);
Human found = humanService.getOneHumanById(1L);
assertThat(found.getFirstName()).isEqualTo("Bill");
}
2. How can I test getOneHumanRandom() method?
Service not use repository when call this method. I can make this method mock, but what will it give?
when(humanService.getOneHumanRandom()).thenReturn(human);
...
#Test
public void getOneHumanRandom() {
Human found = humanService.getOneHumanRandom();
assertThat(found.getFirstName()).isEqualTo("Bill");
}
I just copy the logic from the service in the test class. What is the point of such testing and is it necessary?

1. Where should I fill the objects? I saw different implementations
I would use #Before for any common setup between all / most tests. Any setup that is specific to a certain test should be go into that test method. If there is a common setup between some, but not all, of your tests you can write private setup method(s).
Remember to keep your tests / code DRY (dont repeat yourself!). Tests have a maintenance factor and keeping common code together with help alleviate some headaches in the future.
2. How can I test getOneHumanRandom() method?
You should create a Human toString() method. This method should concat all the properties on your Human object. You can call getOneHumanRandom() twice and assert that they are not equal.
#Test
public void getOneHumanRandomTest()
{
// SETUP / TEST
Human one = service.getOneHumanRandom();
Human two = service.getOneHumanRandom();
// VERIFY / ASSERT
assertNotEquals("these two objects should not be equal", one.toString(), two.toString())
}
Hope this helps!

Related

Mocking Java Constant variables (public static) using Mockito/JUnit

I need to mock the constant variable in order to test one of my method. How can I do it with Mokito and Junit.
#Component( "mybean" )
#org.springframework.context.annotation.Scope( value="session" )
public class MyBean {
Public void methodToBeTested() {
if (!AppConst.SOME_CONST.equals(obj.getCostCode())) {
// some logic
}
}
}
AppConst class
#Configuration
public class AppConst
{
public static String SOME_CONST;
public static String HOST_URL;
#PostConstruct
public void postConstruct()
{
SOME_CONST = "My Code";
HOST_URL = "Some URL";
}
}
So, from my junit test class, how can I mock the AppConst and it's variables? Now, when I run it, i hit a nullpointer error.
Can this be done with powermock? if yes please give some sample
Mockito version I use.
compile "org.mockito:mockito-all:1.9.5"
compile "org.powermock:powermock-mockito-release-full:1.6.1"
Instead of mocking there would be another solution to be able to test it:
public void methodToBeTested(SomeObject obj) {
performLogic(AppConst.SOME_CONST, obj);
}
boolean performLogic(String check, SomeObject testObj) {
if (!check.equals(obj.getCostCode())) {
// some logic
return true;
}
return false;
}
That way you can test two things, both combined show you that your code works as intended:
public void testMethodToBeTested() {
MyBean mb = new MyBean() {
#Override
void performLogic(String check, SomeObject testObj) {
assertSame("check constant is passed", AppConst.SOME_CONST, check);
}
}
mb.methodToBeTested(new SomeObject());
mb = new MyBean();
SomeObject so = createSomeTestObject("My Code"); // not the actual constant but an equal String
assertFalse("check some logic not occurred", mb.performLogic("My Code", so));
so = createSomeTestObject("Not the constant");
assertFalse("check some logic not occurred", mb.performLogic("Not the constant", so));
assertTrue("check some logic occurred", mb.performLogic("My Code", so));
// additional tests covering the actual logic
}
Another solution could be putting the condition of the if-statement into its own method, e.g. shouldLogicOccurr(String check) and test that method individually.
In other words: Sometimes it's necessary to refactor your code to make tests easier or sometimes even possible at all. A good side effect is the next time you implement something you already have testability in mind and create your code suitable for this in the first place.
Mocking is a good way to get things under test that use third party libraries that can't be changed and have too many dependencies to be set up but if you end up using that for your own code you've got a design issue.

Mockito, verify that line not executed for some condition inside one test method

Im writing unit test using testng and mockito.
Im normally practice to call one method few times inside same test method by using different values / condition to check all scenarios.
Please dont think about the logic, and design i have provided. this is just sample for clear what actually i want to make.
Review code below.
public class Human {
private String name;
private boolean parent;
private List<Human> childs = new ArrayList<>();
public String getName() {
return name;
}
public boolean isParent() {
return parent;
}
public void setParent(boolean parent) {
this.parent = parent;
}
public void addChild(List<Human> childs) {
this.childs = childs;
}
public List<Human> getChilds() {
return childs;
}
}
public class Validator {
public boolean isParent(Human human) {
if (null == human) {
return false;
}
if (human.isParent()) {
return true;
}
if (human.getChilds().size() > 0) {
return true;
}
return false;
}
}
Im writing test case for Validator isParent method by using mockito.
public class ValidatorTest {
public void testIsParent() throws Exception {
Validator validator = Mockito.spy(new Validator());
Human human = Mockito.mock(Human.class);
Mockito.when(human.isParent()).thenReturn(false);
boolean isParent = validator.isParent(human);
Mockito.verify(human).getChilds();
Mockito.when(human.isParent()).thenReturn(true);
isParent = validator.isParent(human);
Mockito.verify(human).getChilds();
}
In here i want to verify that getChilds() never call for second method call to validator.isParent(human) because mocked human set to return true when call human.isParent();
I used Mockito.verifyZeroInteractions() but it says fail
As i understand Mockito.verifyZeroInteractions() check through all test. not only for particular method call.
I want to know is there some way to verify that method is not call for some cases and method call for same cases within same test method.
Or should i should practice test one scenario in one test method.
It's a good practice to have "one scenario per one one test method" (see How many unit tests should I write per function/method? )
Technically it's still possible to reset mocks with Mockito.reset(...), but this what official documentation says about it:
Smart Mockito users hardly use this feature because they know it could be a sign of poor tests.
Normally, you don't need to reset your mocks, just create new mocks for each test method.
Instead of reset() please consider writing simple, small and focused test methods over lengthy, over-specified tests. First potential code smell is reset() in the middle of the test method. This probably means you're testing too much. Follow the whisper of your test methods: "Please keep us small & focused on single behavior".
See https://static.javadoc.io/org.mockito/mockito-core/2.9.0/org/mockito/Mockito.html#17
The verify method can accept a second argument where you can specify how many times the method has been called. You can use this to say the method was never called, called once, twice etc.
For example:
import static org.mockito.Mockito.never;
...
public void testIsParent() throws Exception {
Validator validator = Mockito.spy(new Validator());
Human human = Mockito.mock(Human.class);
Mockito.when(human.isParent()).thenReturn(false);
boolean isParent = validator.isParent(human);
Mockito.verify(human).getChilds();
Mockito.when(human.isParent()).thenReturn(true);
isParent = validator.isParent(human);
Mockito.verify(human, never()).getChilds();
}
The documentation for this is here: http://static.javadoc.io/org.mockito/mockito-core/2.9.0/org/mockito/Mockito.html#4
I want to point out that this question seriously abuses mocking, for testing something that can easily and cleanly be tested without any mocks.
This is what the tests should look like:
public class ValidatorTest {
final Validator sut = new Validator();
#Test
public void checkThatNoHumanIsNotAParent() {
boolean isParent = sut.isParent(null);
assertFalse(isParent);
}
#Test
public void checkHumanThatIsNotAParent() {
Human notAParent = new Human();
boolean isParent = sut.isParent(notAParent);
assertFalse(isParent);
}
#Test
public void checkParentHumanWithNoChildIsAParent() {
Human parentWithNoChildren = new Human();
parentWithNoChildren.setParent(true);
boolean isParent = sut.isParent(parentWithNoChildren);
assertTrue(isParent);
}
#Test
public void checkHumanNotMarkedAsParentButWithChildIsAParent() {
Human humanWithChildren = new Human();
Human child = new Human();
humanWithChildren.addChild(child);
boolean isParent = sut.isParent(humanWithChildren);
assertTrue(isParent);
}
}
These tests completelly exercise all four scenarios. They are clearly much better than a version that uses mocking. Finally, note that Mockito's documentation (in the page on how to write good tests) also says that value objects (such as Human) should not be mocked.

TestNG dataproviders with a #BeforeClass

I am trying to run a class with multiple tests under two different conditions. Basically I have a bunch of tests related to a search. I am adding new functionality of a new search strategy, and in the meantime want to run the already written tests under both configurations. As we have multiple classes each with multiple tests I want to streamline this process as much as possible. Ideally it'd be great to do the setup in a #BeforeClass with a data provider so that all tests in the class are basically run twice under the different configurations, but doesn't look like this is possible.
Right now I have:
public class SearchTest1 {
#Test(dataProvider = "SearchType")
public void test1(SearchType searchType) {
setSearchType(searchType);
//Do the test1 logic
}
#Test(dataProvider = "SearchType")
public void test2(SearchType searchType) {
setSearchType(searchType);
//Do the test2 logic
}
#DataProvider(name = "SearchType")
public Object[][] createData() {
return new Object[][]{
new Object[] {SearchType.scheme1, SearchType.scheme2}
}
}
}
Is there a better way to do this?
If you want to avoid having to annotate each and every method with the data provider, you can use a Factory instead.
public class SearchTest1 {
private final SearchType searchType;
public SearchTest1( SearchType searchType ) {
this.searchType = searchType;
}
#Test
public void test2() {
//Do the test2 logic
}
...
}
And your factory class will be:
public class SearchTestFactory {
#Factory
public Object [] createInstances() {
return new Object[] { new SeartchTest1( SearchType.ONE ), new SearchTest1( SearchType.TWO ) };
}
}
See more on this here.
Then you can either have one factory that enumerates every test class or a separate factory for each, the first one is obviously less flexible, the second one means slightly more code.
You can use parameters in #BeforeClass. Just use (with some cleanup)
context.getCurrentXmlTest().getParameters()
#SuppressWarnings("deprecation")
#BeforeClass
public void setUp(ITestContext context) {
System.out.println(context.getCurrentXmlTest().getAllParameters());
}

How can I test if this object returns the correct String?

I have a Thing class that holds various Strings and a Collection of Stuff.
I have to test whether the Message class can correctly return item as "found" in a MessageTest class.
What is the most elegant/efficient way of testing this?
What kind of fake object should I create, and how do I test with/against it?
public class Thing
{
public String a;
public String b;
public String c;
public String d;
public String e;
public Collection<Stuff> collection;
}
public class Message
{
private String item;
public Message(Thing thing)
{
for (Stuff stuff : thing.collection)
{
if (stuff.getItem().equals("key"))
{
item = "found";
}
}
}
#Override
public String toString()
{
StringBuilder builder = new StringBuilder();
builder.append(a);
builder.append(b);
builder.append(c);
builder.append(d);
builder.append(e);
builder.append(item);
return builder.toString();
}
}
The idea of JUnit is to do unit testing. I think you should test a specific method that you know what it should do instead of "faking" anything (I understand mocking for faking), for example:
import static org.junit.Assert.assertEquals;
import org.junit.Test;
public class PayrollTesCase {
#Test
public void testCalculate() {
Message m = new Message(new Thing("a", "b", "c", "d", "e"));
assertEquals("abcde", m.toString());
}
}
So, if in the future you change your Message.toString() implementation, you introduced a bug and it returns "cde" then your specific test case will fail. This is the idea behind unit testing.
I like this definition:
Unit Testing is a level of the software testing process where
individual units/components of a software/system are tested. The
purpose is to validate that each unit of the software performs as
designed.
In JUnit 4.x all the methods are annotated. So, the flow of control in JUnit methods can be described as below:
#BeforeClass -> #Before -> #Test -> #After -> #AfterClass
By the way, you have different degrees of testing, Unit Testing is the "smallest". I'm not sure what you need but maybe you want to do an Integration Test.

Passing JUnit data between tests

I just discovered when creating some CRUD tests that you can't set data in one test and have it read in another test (data is set back to its initialization between each test).
All I'm trying to do is (C)reate an object with one test, and (R)ead it with the next. Does JUnit have a way to do this, or is it ideologically coded such that tests are not allowed to depend on each other?
Well, for unit tests your aim should be to test the smallest isolated piece of code, usually method by method.
So testCreate() is a test case and testRead() is another. However, there is nothing that stops you from creating a testCreateAndRead() to test the two functions together. But then if the test fails, which code unit does the test fail at? You don't know. Those kind of tests are more like integration test, which should be treated differently.
If you really want to do it, you can create a static class variable to store the object created by testCreate(), then use it in testRead().
As I have no idea what version of Junit you talking about, I just pick up the ancient one Junit 3.8:
Utterly ugly but works:
public class Test extends TestCase{
static String stuff;
public void testCreate(){
stuff = "abc";
}
public void testRead(){
assertEquals(stuff, "abc");
}
}
JUnit promotes independent tests. One option would be to put the two logical tests into one #Test method.
TestNG was partly created to allow these kinds of dependencies among tests. It enforces local declarations of test dependencies -- it runs tests in a valid order, and does not run tests that depend on a failed test. See http://testng.org/doc/documentation-main.html#dependent-methods for examples.
JUnit is independent test. But, If you have no ways, you can use "static" instance to store it.
static String storage;
#Test
public void method1() {
storage = "Hello"
}
#Test
public void method2() {
Assert.assertThat(something, is(storage));
}
How much processing time do these tests take? If not a lot, then why sweat it. Sure you will create some object unnecessarily, but how much does this cost you?
#Test
void testCreateObject() {
Object obj = unit.createObject();
}
#Test
void testReadObject() {
Object obj = null;
try {
obj = unit.createObject(); // this duplicates tests aleady done
} catch (Exception cause) {
assumeNoException(cause);
}
unit.readObject(obj);
}
in this basic example, the variable is changed in the test A, and can be used in the test B
public class BasicTest extends ActivityInstrumentationTestCase2 {
public BasicTest() throws ClassNotFoundException {
super(TARGET_PACKAGE_ID, launcherActivityClass);
}
public static class MyClass {
public static String myvar = null;
public void set(String s) {
myvar = s;
}
public String get() {
return myvar;
}
}
private MyClass sharedVar;
#Override
protected void setUp() throws Exception {
sharedVar = new MyClass();
}
public void test_A() {
Log.d(S,"run A");
sharedVar.set("blah");
}
public void test_B() {
Log.d(S,"run B");
Log.i(S,"sharedVar is: " + sharedVar.get());
}
}
output result is:
run A
run B
sharedVar is: blah

Categories