2

In some case it's needed to clean up or reset the mocks between tests cases.

Using Kotling with JUnit5 and Mockk, a first approach should be like this:

class CreateProductsTests {

    @Test
    fun `run() with an existing product should throw a CrudException`() {

        val productRepository = mockk<ProductRepository>()
        val editorService = mockk<EditorService>()
        val sut = CreateProductServiceImpl(productRepository, editorService)

        // Given an editor that return a JSON
        val product = completeTestProduct()
        every { editorService.edit("Create new Product", product) } returns product

        // And the product does exist in the database
        every { productRepository.findById(product.id) } returns Optional.of(product)

        // When we call createProduct()"
        // Then should fail
        val exception = assertFailsWith<CrudException> { sut.createProduct() }
        exception.message shouldBe "The product 'TST' already exists in database"
    }

    @Test
    fun `createProduct() with an invalid product should fail`() {

        val productRepository = mockk<ProductRepository>()
        val editorService = mockk<EditorService>()
        val sut = CreateProductServiceImpl(productRepository, editorService)

        // Given an editor that return a JSON
        val product = completeTestProduct()
        every { editorService.edit("Create new Product", product) } returns product

        // And the product does exist in the database
        every { productRepository.findById(product.id) } returns Optional.of(product)

        // And a repository saves the product
        every { productRepository.save(product) } returns product

        // When we call createProduct()"
        val actual = sut.createProduct()

        // Then it should return the product
        actual shouldBe product

        // And should call once these dependencies
        verify(exactly = 1) {
            editorService.edit(any<String>(), any<Product>())
            productRepository.findById(any<String>())
            productRepository.save(any<Product>())
        }
    }
}

But instead of declaring the mocks on every test case, and initializing the SUT, maybe it's clearer (maybe not faster) to use @BeforeEach, with something like this:

class CreateProductsTests {

    var productRepository = mockk<ProductRepository>()
    var editorService = mockk<EditorService>()
    var sut = CreateProductServiceImpl(productRepository, editorService)

    @BeforeEach
    fun clear() {
        productRepository = mockk<ProductRepository>()
        editorService = mockk<EditorService>()
        sut = CreateProductServiceImpl(productRepository, editorService)
    }
...

Is there any better (and faster) way to declare the mocks and sut once and reset or clear all of them on every tests?

p3quod
  • 1,449
  • 3
  • 13
  • 15

2 Answers2

9

You can initialize the mocks and sut once and reset the mocks before each test but you need to spawn only once instance of your test class. This is how it would look:

@TestInstance(Lifecycle.PER_CLASS)
class CreateProductsTests {

    var productRepository = mockk<ProductRepository>()
    var editorService = mockk<EditorService>()
    var sut = CreateProductServiceImpl(productRepository, editorService)

    @BeforeAll
    fun setup() {
        MockKAnnotations.init(this)
        sut = CreateProductServiceImpl(productRepository, editorService)
    }

    @BeforeEach
    fun clear() {
        clearMocks(productRepository, editorService)
    }
...
Alfred Afutu
  • 191
  • 3
7

You should try that :

 @AfterEach
internal fun tearDown() {
    clearAllMocks()
}