I think my problem is related to this issue:
Tests pass when run individually but not when the whole test class run
My tests all pass individually but when i run the test class, two of them fail. Debugging shows that that assertEquals is expecting different states than given in the code, mainly the states from different tests. The solution should be using a #Before or #After and do a cleanup, but i am having troubles using that becouse the instance is Autowired from Spring.
Main question: Is my thinking right and how do i 'reset' the autowired instance?
#SpringBootTest
class StatementProcessorServiceTest {
#Autowired
StatementProcessorService statementProcessorService;
private StatementProcessorInputModel setup(long id, boolean endbalanceCorrect) {
StatementProcessorInputModel statementProcessorInputModel = new StatementProcessorInputModel();
statementProcessorInputModel.startBalance = 1.00;
statementProcessorInputModel.id= id;
statementProcessorInputModel.startBalance = 1.00;
statementProcessorInputModel.mutation = 1.00;
statementProcessorInputModel.endBalance = endbalanceCorrect ? 2.00 : 1.00;
return statementProcessorInputModel;
}
#Test
void verify_with_all_good_settings()
{
StatementProcessorResponseModel sut;
sut = statementProcessorService.validate(setup(1, true));
Assert.assertEquals(sut.result, "SUCCESSFUL");
}
#Test
void verify_with_good_settings_but_duplicated_reference()
{
StatementProcessorResponseModel sutFirst = statementProcessorService.validate(setup(2, true));
Assert.assertEquals(sutFirst.result, "SUCCESSFUL");
StatementProcessorResponseModel sutSecond;
sutSecond = statementProcessorService.validate(setup(2, true));
Assert.assertEquals(sutSecond.result, "DUPLICATE_REFERENCE");
}
#Test
void verify_with_wrong_endBalance_and_good_reference()
{
StatementProcessorResponseModel sut = statementProcessorService.validate(setup(3, false));
Assert.assertEquals(sut.result, "INCORRECT_END_BALANCE");
}
#Test
void verify_with_all_wrong_settings()
{
StatementProcessorResponseModel sutFirst = statementProcessorService.validate(setup(4, true));
Assert.assertEquals(sutFirst.result, "SUCCESSFUL");
StatementProcessorResponseModel sutSecond = statementProcessorService.validate(setup(4, false));
Assert.assertEquals(sutSecond.result, "DUPLICATE_REFERENCE_INCORRECT_END_BALANCE");
}
}
Edit 1: Added Service code
#Component
public class StatementProcessorService {
private StatementProcessorResponseModel responseModel = new StatementProcessorResponseModel();
private static ArrayList<Long> usedReferences = new ArrayList<>();
public StatementProcessorResponseModel validate(StatementProcessorInputModel inputModel){
if(!validateUniqueReference(inputModel.transactionReference))
{
responseModel.errorRecords.add("account_number_of_ inCorrectEndBalance _record");
responseModel.result = "DUPLICATE_REFERENCE";
}
if(!validateMutation(inputModel))
{
responseModel.errorRecords.add("account_number_of_ inCorrectEndBalance _record");
if (!responseModel.result.isBlank()){
responseModel.result = responseModel.result + "_INCORRECT_END_BALANCE";
}
else{
responseModel.result = "INCORRECT_END_BALANCE";
}
}
if (responseModel.result.isBlank()){
responseModel.result = "SUCCESSFUL";
}
return responseModel;
}
If you mark your test cases/classes which modify the database with #Transactional then the test runner will rollback the transaction after the test.
You can refer to the reference documentation regarding test transactions.
It seems you're not using a database to store state, so it's difficult to add a complete answer without knowing how you are storing this state. Regardless StatementProcessorService must be storing state somehow, so I'll try to illustrate how you can reset this state between tests, and perhaps you can adapt it to your situation.
Starting from a basic "it's stored in memory" example
class StatementProcessorService {
// assuming you're storing state in memory for now
// here we're just creating a shared map, which is where we presume you're putting data
private Map<String, String> state = new HashMap<>();
}
The easiest way of doing this is to expose a method to reset the state from outside the StatementProcessorService.
class StatementProcessorService {
// ...
/** Reset state */
#VisibleForTesting
public void reset() { state.clear(); }
}
You could also use an injected state holder, which during tests can itself expose a reset method
class StateHolder {
// accessor/mutator methods
}
class TestStateHolder extends StateHolder {
// ...
public void reset() { ... }
}
class StatementProcessorService {
#Autowired
private StateHolder state;
}
And finally, you could just mock the StateHolder with Mockito
#SpringBootTest
class StatementProcessorServiceTest {
#MockBean StateHolder state;
}
As normal with Spring, there are a lot of ways of achieving your goal.
Related
I have a class:
classA
{
private Boolean isEnable;
private final Config config;
public classA(final Config config)
{
this.config = config;
isEnable = config.getEnablingStatus();
}
public classB fun()
{
// Do something!
// Return an object of classB!
}
}
I want to test the method fun() in 2 different scenarios - one in which isEnable is true, and one in which it's false.
So, to do that I am thinking of having 2 different instances of the classA in its test class:
TestClassForClassA
{
private Boolean isEnable;
#Mock
private final Config config;
#InjectMocks
classA objAWithIsEnableAsTrue, objAWithIsEnableAsFalse;
#Before
public void init()
{
initMocks(this);
}
public void testFunWhenIsEnableIsTrue()
{
doReturn(true).when(config).getEnablingStatus();
objAWithIsEnableAsTrue = new classA(config);
// Use the objAWithIsEnableAsTrue object in this test method.
}
public void testFunWhenIsEnableIsFalse()
{
doReturn(false).when(config).getEnableStatus();
objAWithIsEnableAsFalse = new classA(config);
// Use the objAWithIsEnableAsFalse object in this test method.
}
}
Can it be done like this? If not what is the right way to do it?
This is not the answer to the question you wrote but I guess it could be the answer you need:
From the code you posted there is no reason to mock classA. In the #Before annotated method you even instantiate the objects by calling the constructor. This means those objects (objAWithIsEnableAsTrue and objAWithIsEnableAsFalse) are not even mocked in your tests.
In order to make your tests more clear, I would suggest moving the setup logic of your tests into the test methods themselves.
TestClassForClassA {
#Test
public void testFunWhenIsEnableIsTrue(){
Config config = mock(Config.class);
doReturn(true).when(config).getEnableStatus();
classA objAWithIsEnableAsTrue = new classA(config);
// Use the objAWithIsEnableAsTrue object in this test method.
}
#Test
public void testFunWhenIsEnableIsFalse(){
Config config = mock(Config.class);
doReturn(false).when(config).getEnableStatus();
classA objAWithIsEnableAsfalse = new classA(config);
// Use the objAWithIsEnableAsTrue object in this test method.
}
}
Also, you should check if you really need to mock the Config object. Can it also be instantiated without side effects? If yes, I would do so to get rid of the whole mocking.
Config config = new Config();
config.setEnablingStatus(true); // maybe do this instead of mocking?
I working on writing tests for a crud application. I need to test the service and repository for Delete and Update statements. How would I go about mocking the repository for delete and update since they won't be returning data?
For example:
#Override
public void makeUserActive(long userId) {
try {
Optional<UserEntity> userEntityList = usersJpaRepository.findById(userId);
UserEntity userEntity = userEntityList.get();
userEntity.setIsActive(1);
usersJpaRepository.save(userEntity);
} catch (Exception e) {
logger.error("Cant make active user", e);
}
}
How do i test the service that mocks this repository and also the repository itself since it wont be returning a value
The question is what is the thing you want to be tested?
If you would like to test your repository you can achieve this by using Springs #DataJpaTest. see Integration Testing With #DataJpaTest
If you would like to test the logic inside your makeUserActive-Method you must make sure to mock your repository.
Assuming the service which contains your makeUserActive-Method looks something like this:
public class UserService{
private final UsersJpaRepository usersJpaRepository;
public UserService(UsersJpaRepository usersJpaRepository) {
this.usersJpaRepository = usersJpaRepository;
}
public void makeUserActive(long userId){
// your code from your question
}
}
You could write your Unit Test like this:
#Test
void makeUserActiveTest(){
UsersJpaRepository repository = new InMemoryUsersJpaRepository();
UserEntity user = new UserEntity();
user = repository.save(user);
UserService service = new UserService(repository);
service.makeUserActive(user.getId());
Optional<UserEntity> activatedUser = repository.findById(user.getId());
assertTrue(activatedUser.isPresent());
assertEquals(1, activatedUser.get().isActive());
}
The InMemoryUsersJpaRepository is a self written Mock which will store all data in an internal Map. The code could look something like this:
public class InMemoryUsersJpaRepository extends UsersJpaRepository {
private Map<Long, UserEntity> users = new HashMap<>();
private Long idCounter = 1L;
#Override
public UserEntity save(UserEntity user) {
if(user.getId() == null){
user.setId(idCounter);
idCounter++;
}
users.put(user.getId(), user);
return user;
}
#Override
public Optional<UserEntity> findById(long userId) {
return Optional.of(users.get(userId));
}
}
This way you will test the logic of your makeUserActive-Method which is currently to simply set the isActivated Flag on you UserEntity.
Also I would like to warn you about the answer of Mensur Qulami.
The Code in his answer will lead to a passing test but I'am pretty sure it does not test the thing you want to be tested.
You should always test the expected and observable behaviour of your method.
In your case this would be the isActivated Flag that should be 1.
The fact that your makeUserActive-Method calls the findById and save Method of the UsersJpaRepository is a mere implementation detail and the testing of those generally lead to brittle tests.
For the methods returning void, you can simply verify that they have been called. Here's an example, that mocks both an object returning method and void returning method.
#ExtendWith(MockitoExtension.class)
class ServiceTest {
#Mock
private Repository repository;
#InjectMocks
private Service service; // assume that this is your class
#Test
void testMakeUserActive() {
// given:
final UserEntity userEntity = new UserEntity();
// mocks:
when(repository.findById(1)).thenReturn(Optional.of(userEntity));
// when:
service.makeUserActive(1);
// then:
verify(repository).findById(1);
verify(repository).save(userEntity);
}
}
I've written a class which reads the entire file and returns the content.
class ClassToTest {
public methodToTest(String input) {
return privateMethod(input);
}
private privateMethod(input) {
ClassPathResource classPathResource = new ClassPathResource(input);
IOUtils.toString(classPathResource.getFile());
}
}
Now, inside my test class, I don't want my test to actually read the file from so I'm trying to mock the method classPathResource.getFile() but somehow I'm not able to do so without writing PrepareForTests() and if I do that those test are not counted in JaCoCo.
I've written test case as
#Test
public void test_methodToTest() {
mockStatic(IOUtils.class);
when(IOUtils.toString(any()).thenReturn("DUMMY_STRING");
methodToTesT("file1.txt");
...
}
The problem is IOUtils.toString gets mocked properly but the call classPathResource.getFile() tries to access the file on the disk. For this, I can do this
PowerMockito.whenNew(ClassPathResource.class)
.withAnyArguments().thenReturn(mockedClassPath);
And add annotation to my test class as
#PrepareForTest(ClassToTest.class)
class MyTestClass {
...
}
But now the problem is this test class is skipped from the JACOCO test coverage . How can I write tests for this class?
You can pass a mocked reference into the constructor doing this:
class ClassToTest {
private ClassPathResource classPathResource;
public ClassToTest(ClassPathResource classPathResource) {
this.classPathResource = classPathResource;
}
public methodToTest(String input) {
IOUtils.toString(classPathResource.getFile(input));
}
}
Or you can pass the mocked reference into the method doing this:
class ClassToTest {
public methodToTest(ClassPathResource classPathResource) {
IOUtils.toString(classPathResource.getFile());
}
}
Having to mock a private member should be seen as a code smell and an indication that something is wrong with the current design. Because ClassPathResource is being initialized internal to the subject class it is now tightly coupled to that class. While not entirely impossible to mock it does make testing the class cleanly more difficult. Consider inverting the creation of the class to a delegate as a dependency.
public interface PathResource {
String getFile(String input);
}
This will allow the injection of the dependency
class ClassToTest {
private classPathResource;
public ClassToTest (PathResource resource) {
this.classPathResource = resource;
}
public String methodToTest(String input) {
return privateMethod(input);
}
private String privateMethod(String input) {
return IOUtils.toString(classPathResource.getFile(input));
}
}
and the dependency can be mocked/faked/stubbed when testing.
public void Test() {
//Arrange
//mock creation
PathResource resource = mock(PathResource.class);
String input = "path";
String expected = "expected_output";
//stubbing
when(resource.getFile(input)).thenReturn(expected);
ClassToTest subject = new ClassToTest(resource);
//Act
String actual = subject.methodToTest(input);
//Assert
verify(resource).getFile(input);
assertEquals(expected, actual);
}
in production code the ClassPathResource would be derived from the abstraction
public class ClassPathResource implements PathResource {
//...code removed for brevity
}
and it would be associated with the abstraction at the composition root.
Following the above suggestions would now allow ClassToTest to be tested in isolation without any knock on effects of implementation concerns.
At first I want to sorry for my english.
I started to make some unit tests (i've never done this before, i'm a new guy in programming).
I have to test simple adding product to database (DynamoDB) method using mockito.verify but I have
"Wanted but not invoked. Actually, there were zero interactions with this mock."
Error and I don't know what to do.
This is my method code (in KitchenService class):
public Product addProduct(Product content) {
ObjectMapper objectMapper = new ObjectMapper();
String mediaJSON = null;
String authorJSON = null;
String productKindsJSON = null;
try {
mediaJSON = objectMapper.writeValueAsString(content.getMedia());
authorJSON = objectMapper.writeValueAsString(content.getAuthor());
productKindsJSON = objectMapper.writeValueAsString(content.getProductKinds());
} catch (JsonProcessingException e) {
logger.log(e.getMessage());
}
Item item = new Item()
.withPrimaryKey("id", UUID.randomUUID().toString())
.with("name", content.getName())
.with("calories", content.getCalories())
.with("fat", content.getFat())
.with("carbo", content.getCarbo())
.with("protein", content.getProtein())
.with("productKinds", productKindsJSON)
.with("author", authorJSON)
.with("media", mediaJSON)
.with("approved", content.getApproved());
Item save = databaseController.saveProduct(PRODUCT_TABLE, item);
logger.log(save + " created");
return content;
}
And this is test code:
#Test
public void addProduct() throws Exception {
KitchenService instance = mock(KitchenService.class);
Product expectedProduct = new Product();
expectedProduct.setName("kaszanka");
expectedProduct.setCalories(1000);
expectedProduct.setFat(40.00);
expectedProduct.setCarbo(20.00);
expectedProduct.setProtein(40.00);
expectedProduct.setProductKinds(Collections.singletonList(ProductKind.MEAT));
expectedProduct.setApproved(false);
Author expectedAuthor = new Author();
expectedAuthor.setId("testID");
expectedAuthor.setName("Endrju Golota");
expectedProduct.setAuthor(expectedAuthor);
Media expectedMedia = new Media();
expectedMedia.setMediaType(MediaType.IMAGE);
expectedMedia.setName("dupajasia");
expectedMedia.setUrl("http://blabla.pl");
expectedProduct.setMedia(expectedMedia);
verify(instance, times(1)).addProduct(expectedProduct);
}
This is what I got after test:
Wanted but not invoked:
kitchenService.addProduct(
model.kitchen.Product#a0136253
);
-> at service.kitchen.KitchenServiceTest.addProduct(KitchenServiceTest.java:80)
Actually, there were zero interactions with this mock.
Can someone tell me what im doing wrong?
What you should mock and verify is the databaseController dependency:
#Test
public void addProduct() throws Exception {
KitchenService instance = new KitchenService(); // you should create the class under test
DatabaseController controllerMock = mock(DatabaseController.class); // mock the controller
instance.setController(controller); // inject the mock
...
// Act
instance.addProduct(expectedProduct);
// Assert
verify(controller).saveProduct(Mockito.eq(PRODUCT_TABLE), Mockito.any(Item.class));
}
You should verify that the database is called within the service.. checking that it was invoked with any Item object should be enough.
Mocking is a tool that you only use for dependencies of the class that is being tested.
It appears that your test does not care about the Author, Media, and Product objects,
these are just dependencies of the method you want to test;
mock them.
Organization will greatly help your test;
do something like this:
public class TestKitchenService
{
private static String VALUE_PRODUCT_NAME = "VALUE_PRODUCT_NAME";
... use constants for other values as well. The value of the constant does not matter.
#InjectMocks
private KitchenService classToTest;
private InOrder inOrder;
#Mock
private Author mockAuthor;
#Mock
private DatabaseController mockDatabaseController;
#Mock
private Logger mockLogger;
#Mock
private Media mockMedia;
#Mock
private Product mockProduct;
#After
public void afterTest()
{
inOrder.verifyNoMoreInteractions();
verifyNoMoreInteractions(mockAuthor);
verifyNoMoreInteractions(mockDatabaseController);
verifyNoMoreInteractions(mockLogger);
verifyNoMoreInteractions(mockMedia);
verifyNoMoreInteractions(mockProduct);
}
#Before
public void beforeTest()
{
MockitoAnnotations.initMocks(this);
doReturn(mockAuthor).when(mockProduct).getAuthor();
doReturn(mockMedia).when(mockProduct).getMedia();
doReturn(VALUE_PRODUCT_NAME).when(mockProduct).getName();
doReturn(Collections.singletonList(ProductKind.MEAT)).when(mockProduct).getProductKinds();
... doReturns for the other product values.
inOrder = inOrder(
mockAuthor,
mockDatabaseController,
mockLogger,
mockMedia,
mockProduct);
ReflectionTestUtils.setField(
classToTest,
"databaseController",
mockDatabaseController);
ReflectionTestUtils.setField(
classToTest,
"logger",
mockLogger);
}
#Test
public void addProduct_success()
{
final Product actualResult;
actualResult = classToTest.addProduct(mockProduct);
assertEquals(
mockProduct,
actualResult);
inOrder.verify(mockProduct).getMedia();
inOrder.verify(mockProduct).getAuthor();
inOrder.verify(mockProduct).getProductKinds();
inOrder.verify(mockProduct).getName();
... inOrder.verify for the other product values.
inOrder.verify(mockDatabaseController).saveProduct(
eq(PRODUCT_TABLE),
any(Item.class));
}
}
The only things that should be mocked -- if anything -- are the ObjectMapper and databaseController. One only mocks collaborator objects, and almost never the system/class under test (very rare cases exist for "spying" on the SUT). And depending on what the ObjectMapper is and how transparent it's operation is, you may not really want to even mock that. Furthermore, as your implementation code is written instantiating the ObjectMapper by directly calling a constructor, you can't even mock it.
While I love the use of Mockito and mock objects, sometimes it is worthwhile to simply test with as many real objects as possible. This is especially true when your collaborators are simple, straightforward, have no side effects, and don't require complex initialization or setup. Only use mocks when it simplifies the test setup or verification.
I have foolowing class which I would like to mock:
BusineesLayer:
/**
* Created by Alexandr on 14.05.2016.
*/
public class BusineesLayer {
public OrderModel orderModel;
public DbService dbService;
...
public BusineesLayer(OrderModel orderModel,DbService dbService) {
this.orderModel = orderModel;
dbService = dbService;
}
public BusineesLayer() {
}
public boolean checkItemsInDb(List<Items> items) throws HandleOrderExeption {
...
return result
}
public boolean handleOrder() throws HandleOrderExeption {
checkItemsInDb(orderModel.getItemsList());
boolean res =dbService.addOrder(orderModel.getItemsList(),
orderModel.getCustumerName(),
countTotalSum(orderModel.getItemsList())
);
return res;
}
}
I would like to test hanldeOrder() method and in order to make it less excess insted of checkItemsinDb() inside invoke "true";
So, my test looks like this:
#Test
public void handleorderTest() {
...
BusineesLayer layer = Mockito.mock(BusineesLayer.class);
layer.dbService = busineesLayer.dbService;
layer.orderModel = busineesLayer.orderModel;
Mockito.when(layer.checkItemsInDb()).thenReturn(true);
boolean res = layer.handleOrder();
assertThat(res, equalTo(true));
}
but it always return false and doesn't go through handlOrder() at all
Is any ways to solve it? Or how can I refactor my code to test it?
You do not test mocks, you use mocks to help you test.
I think you've just become confused at how you are using mocks. Mocks allow us to simulate and can responses to objects we are testing. If you're testing the handleOrder method, then you should mock anything that interacts with that method, in this case DbService and OrderModel.
#Test
public void handleorderTest() {
BusineesLayer layer = new BusineesLayer(); //we are testing this!
DbService dbService = Mockito.mock(DbService.class);
OrderModel orderModel = Mockito.mock(OrderModel.class);
layer.dbService = dbService;
layer.orderModel = orderModel;
Mockito.when(orderModel.getItemsList()).thenReturn(new ArrayList<Items>());
Mockito.when(dbService.foo()).thenReturn(true);
//mock up multiple calls so your service will provide true
boolean res = layer.handleOrder();
assertThat(res, equalTo(true));
//repeat for false, and so on
}
If, however, you are trying to test the dbService call, then you should create a test for it without the business layer at all. The business layer doesn't depend on anything except calls to other methods, so whether you use those real objects or mocked versions of those objects, the functionality should be the same. The business logic only appears to fail if DBService or OrderModel break, so you would test service and model separately (without involving the business layer) to test those.