DEV Community

loading...
Cover image for My first ever unit test

My first ever unit test

Bassem Ibrahim👨‍💻🇪🇬
Egyptian software engineer and blogger who loves to hike 🏕
・3 min read

I consider myself quite late. Being in the second year of my career and my first ever unit test was a few days ago. Lately, I joined the backend team and they have a test coverage policy. Because of this policy, every developer working on a certain task have to do the unit tests to cover his/her task. I have always liked the idea of having tests acting as a friend that increases your confidence in your work and help you identify bugs 🐞 quickly making your work easier and better. Anyway, my first task in the backend team was to expose a simple GET URL. We are using spring as a development framework, Mockito as the mocking framework and JUnit for testing.
In this post, I would like to share with you my first ever test.

What is unit testing? 🤔

According to Wikipedia, It is “a software testing method by which individual units of source code, sets of one or more computer program modules together with associated control data, usage procedures, and operating procedures, are tested to determine whether they are fit for use”

In order to unit test your application, you have to follow the design principle of having small unit modules that you can test. If you are going to test a small component of your application. it needs to be isolated from all other components. That's where Mockito comes in. That’s where you will have to mock and abstract any external dependencies.
The way I see it is that unit testing follows the divide and conquer methodology :

  1. Isolate your code by mocking external dependencies
  2. Think about every possible scenario
  3. Write your tests for this particular unit covering every scenario
  4. Do this until every unit in your application is tested

My super simple yet super awesome task. 😎

Being my first task ever in spring. I choose the simplest one I could get. The code below is a controller that retrieves a resource from the service layer and based on the response, The controller would respond with a 200 or a 404 response code.

@RequestMapping(method = RequestMethod.GET)
public ResponseEntity<List<Resource>> getAllResource() {
    List<Resource> resourceList = resourceService.getAllResource();

    if (resourceList.size() > 0) {
        return new ResponseEntity<List<Resource>>(resourceList, null, HttpStatus.OK);
    }
    return new ResponseEntity<List<Resource>>(null, null, HttpStatus.NOT_FOUND);
}

Very simple, Isn't it? If you think about it, All the logic in the code above boils down to an IF condition. And that's exactly what we are gonna test.

Let's write tests for that awesome task. 😎

Okay, let's follow the above steps :

  1. Isolate: In that case, we need to isolate the controller by mocking the call to the service layer. This way we divide the cycle into units and then test every unit independently. In this post, I am only testing the controller unit.
  2. Cover every scenario: In that case we have 2, Either we have something to return or we don’t. If the list of the resource returned by the mocked service layer is not empty then the output should be a 200. If not then the output should be a 404.
  3. Writing the tests covering each scenario:

Either the service layer returned a non-empty list, The controller then would return a 200 status code

@Test
public void testGetAllResourceSuccess() {
    List<Resource> resourceList = new ArrayList<Resource>();
    resourceList.add(new Resource());

    // Here we mock the service layer function and set the response to a non-empty list.
    Mockito.when(resourceServiceImpl.getAllResource()).thenReturn(resourceList);
    try {
        // Then we expect the response to be a 200
        mockMvc.perform(get(/resource")).andExpect(status().isOk());
    } catch (Exception e) {}
}

Or the service layer returned an empty list, The controller then would return a 404 status code

@Test
public void testGetAllResourceFail() {
    List<Resource> resourceList = new ArrayList<Resource>();
    // Here we mock the service layer function and set the response to an empty list.
    Mockito.when(resourceServiceImpl.getAllResource()).thenReturn(resourceList);
    try {
        // Then we expect the response to be a 404
        mockMvc.perform(get("/resource")).andExpect(status().isNotFound());
    } catch (Exception e) {}
}

Looks easy, simple and straightforward, Isn't it? Well, that’s how unit tests are.
At first, I felt like that can't be it. I felt that this was too simple, that I will never actually fail. Then after some researching, everything made sense. You don’t actually have to test the whole cycle in one test case. In every test case, you cover a small unit. Doing this everywhere means that every bit on its own is working correctly. That's why it's really important to cover every unit in your project.

Happy coding 🔥🔥

Discussion (22)

Collapse
recursivefaults profile image
Ryan Latta

Hooray for testing!

As time goes on you'll begin to uncover that writing tests is a whole different skillset. Enjoy getting better at it!

As you continue to test, here are a few experiments to consider:

1) Mocking - Used to isolate pieces of code from one another. How would you write this test without mocking? What would the smallest, "Unit" of code that you can test be? What might that tell you about your coupling?

2) Write your test first. Write a test that expresses the wish of your code. Watch it fail, then work to get it to pass. How does your test/implementation code look compared to the other way?

Collapse
bassemmohamed profile image
Bassem Ibrahim👨‍💻🇪🇬 Author

yeah, I read about the test-driven approach, Sounds cool. But I don't feel that many people are using it

Collapse
asparallel profile image
AsParallel

Number of people using is an irrelevant metric. What matters is when (what conditions) you should use it.

If you design your implementation before writing your code (which most any skilled engineer can and should do), TDD is just codification of the design. If you skip that step, then you can't do TDD.

If the number of bugs is irrelevant to the release of the product and it's adoption (ex: internal application with a captive user base), TDD adds cost that may not be worth it.

If you have no/fluctuating requirements and the timeline won't allow for it, it's unlikely you can do TDD (lazy enterprise approach).

That said, even if your team isn't bought into it, that doesn't preclude you from adopting it as a rigorous process that results in you delivering a better product with lower failure rates.

Thread Thread
bassemmohamed profile image
Bassem Ibrahim👨‍💻🇪🇬 Author

I like trying out approaches just for the sake of it, at the least i will try it out in the future. Will definitely share my experience too. Thanks @asparallel

Collapse
alfanse profile image
Alex

Please consider removing the try catch from your example. It risks a false positive test result.

You may simply have the test method signature throw Exception.

Collapse
bassemmohamed profile image
Bassem Ibrahim👨‍💻🇪🇬 Author

Will definitely consider that thanks.

Collapse
cathodion profile image
Dustin King

I was going to say this too. I'm not familiar with Mockito, but test frameworks I've used in the past use exceptions to signal to the test runner when the assertion fails.

Collapse
borgerstoon profile image
Toon Borgers

Another (Spring specific tip): ResponseEntity has helper factory methods to get a response for a specific status. See here for some examples (starting from Furthermore, ResponseEntity provides two nested builder interfaces)

Collapse
bassemmohamed profile image
Bassem Ibrahim👨‍💻🇪🇬 Author

Good point, Didn't know that before.

Collapse
jacksonelfers profile image
Jackson Elfers

Sometimes tests requires more creativity than the actual component being tested. It definitely inspires peace of mind. I test my own libraries more than full projects but I'm considering full coverage down the road. Nice little example. 😁

Collapse
jonrandy profile image
Jon Randy

I've been a professional developer for almost 25 years - never written unit tests

Collapse
asparallel profile image
AsParallel

Pretty common honestly. There's a lot of shops that never got on board with TDD, and probably an equal number that tried, but aren't doing it correctly.

I won't deny they help when done well, but more often than not I've seen unit tests bolted onto the end of a project with poor/no requirements, functioning as little more than code-wise documentation of the state of the system at the time the test was written, rather than its adherence to business logic mandates.

Collapse
bogdaaamn profile image
Bogdan Covrig

Congrats! You've made your first blood, more to come.

Collapse
vorsprung profile image
vorsprung

I think I wrote my first unit test in 2000. That was in Java too. I used the xUnit library

Collapse
bassemmohamed profile image
Bassem Ibrahim👨‍💻🇪🇬 Author

sure

Collapse
Sloan, the sloth mascot
Comment deleted
Collapse
bassemmohamed profile image
Bassem Ibrahim👨‍💻🇪🇬 Author

I am not the one to ask here. Still new to unit testing.

But i would say that you need to split these large functions into smaller units that would make them easier to test.

Collapse
Sloan, the sloth mascot
Comment deleted
bassemmohamed profile image
Bassem Ibrahim👨‍💻🇪🇬 Author

I don't really know any other way than refactoring the code. But i really encourage you to do it. If you have the time. Just take backup and try it out. Best of luck 🤞

Collapse
bassemmohamed profile image
Bassem Ibrahim👨‍💻🇪🇬 Author

Yeah great idea and if it gets long, it won't bother anyone anyway because no one would ever call it.