Categories
discuss

How to mock ModelMapper in Spring?

I’m trying to write unit test for my service layer:

@SpringBootTest
class ClinicServiceTest {

@Mock
private ProcedureRepository procedureRepository;
@InjectMocks
private ClinicService clinicService;

@Test
void setProcedureStatus() {
    when(procedureRepository.findById(1L)).thenReturn(Optional.of(initialEntity));
    when(procedureRepository.saveAndFlush(expectedEntity)).thenReturn(expectedEntity);
    Procedure assertProcedure = clinicService.setProcedureStatus(1L, "CANCELED");
  }
}

When i call setProcedureStatus method it throws NullPointerException because my service class uses ModelMapper that is being autowired by Spring:

@Service
@RequiredArgsConstructor
public class ClinicService {

private final ProcedureRepository procedureRepository;
private final ModelMapper modelMapper;

public Procedure setProcedureStatus(Long procedureId, String status) {
    ...
    return modelMapper.map(procedureEntity, Procedure.class);
}

}

Unit test doesn’t raise Spring context, that’s the reason why ModelMapper is null in Service when i call it from test. Is there ways to solve this problem?

Answer

You want to write a unit test so you shouldn’t raise the spring context. Unit tests are faster than Integration tests (where often you want to raise your spring context). When it comes to unit tests you want to test unit part of your application.

It this case you are getting NPE because you didn’t initialize ModelMapper.

I didn’t see where you are creating your ClinicService, but there is one example how you can avoid that NPE:

@Test
void setProcedureStatus() {
    when(procedureRepository.findById(1L)).thenReturn(Optional.of(initialEntity));
    when(procedureRepository.saveAndFlush(expectedEntity)).thenReturn(expectedEntity);
    // Initialize ClinicService with mocked procedeRepository but real ModelMapper
    ClinicService clinicService = new ClinicService(procedureRepository, new ModelMapper(), ... other dependencies );
    Procedure assertProcedure = clinicService.setProcedureStatus(1L, "CANCELED");
}

or you can mock ModelMapper too.

Another way is to use MockitoJunitRunner and initialize your class under tests like below:

@RunWith(MockitoJUnitRunner.class)
 public class ExampleTest {
 
     @Mock
     private ModelMapper modelMapper;

     @Mock
     private Repository repository;

     @InjectMocks
     private ClinicService classUnderTest;
  

Thanks to dependency injection your code is much simplier to test because you can just create your service with dependencies passed via constructor.

Categories
discuss

React table slow loading when displaying img rows

I am new to React. I am just trying to build a table of data coming from an API which I got it fetching. But now I want to render the data into a table.

My issue is that one of the columns is a logo that comes from a URL index.php?Action=GetLogo&id=51. The number of results is more than 300. Even though I am already using pagination the table rendering is slow, particularly the logo column. All the data gets loaded, but I can see each logo being rendered row by row, giving the user a slow experience.

Is there any way how React can approach to solve this issue? Someone please point me in the right direction how to tackle this.

Thanks everyone.

Update Someone has advised me to use a sync/await function to load the images. Then I am updating the code. However my main issue is still with me: loading the data particularly the logo column. All the data gets rendered, but not the logo column, then each logo starts to render one by one looking very slow. I thought the async/await function would alleviate that.

    import React from 'react';
    import ReactDOM from 'react-dom';
    import FormData from 'form-data';

    class FilterableSupplierTable extends React.Component {
        constructor(props) {
            super(props);
            this.state = {
                suppliers: []
            }
        }

        componentDidMount() {
            let formData = new FormData();
            formData.append('AjaxMethodName', 'GetSuppliers');
            const options = {
                method: 'POST',
                headers: {
                    'Accept': '*/*'
                },
                body: formData
            };
            fetch(`?Model=route_to_controller`, options)
                .then(response => response.json())
                .then(data => {
                    this.setState({ suppliers: JSON.parse(data.response_data) })
                });
        }

        async getLogos(suppliers) {
            return await Promise.all(
                suppliers.map(async supplier => {
                    supplier.new_logo = !!supplier.has_logo ?
                        <img style={{maxWidth: "100px"}} src={supplier.logo} alt={supplier.supplier_id} /> :
                        <i className="icon icon-warning no_logo">&nbsp;No Logo</i>;
                    return supplier;
                });
            );
        }

        render() {
            const rows = [];
            const suppliers = this.state.suppliers;
            this.getLogos(suppliers)
                .then(results => {
                    results.map(supplier => {
                        rows.push(
                            <tr>
                                {/* supplier.logo is index.php?Action=GetLogo&id=51, etc */}
                                <td><img src={supplier.new_logo} /></td>
                                <td>{supplier.name}</td>
                            </tr>
                        );
                    });
                });

            return (
                <table>
                    <thead>
                        <tr>
                            <th colSpan="4">Suppliers</th>
                        </tr>
                        <tr>
                            <th>Logo</th>
                            <th>Name</th>
                        </tr>
                    </thead>
                    <tbody>{rows}</tbody>
                </table>
            );
        }
    }
    ReactDOM.render(
        <FilterableSupplierTable />,
        document.getElementById('suppliers_list')
    );

Answer

Your issue can be solved with a component that updates a “global loading state”.

Only after all images have updated that they finished loading, they become visible together:

function MyImage(props) {
  const onLoad = () => {
    props.onLoad();
  };

  return (
    <div>
      {props.isCurrentlyLoading && <div>Loading</div>}
      <img
        src={props.src}
        onLoad={onLoad}
        style={
          props.isCurrentlyLoading
            ? { width: "0", height: "0" } // You can also use opacity, etc.
            : { width: 100, height: 100 }
        }
      />
    </div>
  );
}

function ImagesBatch() {
  const [loadedImagesCounter, setLoadedImagesCounter] = useState(0);
  const [isAllLoaded, setIsAllLoaded] = useState(false);

  const updateLoading = () => {
    if (loadedImagesCounter + 1 === imagesUrls.length) {
      setIsAllLoaded(true);
    }
    setLoadedImagesCounter(prev => prev + 1);
  };

  return (
    <div>
      {imagesUrls.map((url, index) => {
        return (
          <MyImage
            key={url}
            src={url}
            index={index + 1}
            isCurrentlyLoading={!isAllLoaded}
            onLoad={updateLoading}
          />
        );
      })}
    </div>
  );
}

You can check the full code here (preferably with an open console), where I used an arbitrary ~6MB image, as an example.

Source: stackoverflow
Text is available under the Creative Commons Attribution-ShareAlike License; additional terms may apply. By using this site, you agree to the Privacy Policy, and Copyright Policy. Content is available under CC BY-SA 3.0 unless otherwise noted. The answers/resolutions are collected from stackoverflow, are licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0 © No Copyrights, All Questions are retrived from public domain..