Lấy ngoại lệ con trỏ null khi chế nhạo và gián điệp trong lớp kiểm tra


8
Android Studio 3.5.3
Kotlin 1.3

Tôi đang cố gắng kiểm tra một số mã đơn giản nhưng tôi vẫn nhận được ngoại lệ sau:

IllegalStateException: gsonWrapper.fromJson<Map…ring, String>>() {}.type) must not be null

Tôi đang sử dụng gián điệp và chế nhạo trả lại để nó sẽ trả về null. Như tôi muốn kiểm tra đường dẫn lỗi.

Không chắc chắn liệu tôi có làm điều gì sai trái với sự bướng bỉnh của mình hay không. Nhưng dường như không thể giải quyết ngoại lệ này.

Sử dụng một lớp bao bọc để bọc thực hiện gson và gián điệp về điều này trong thử nghiệm

public class GsonWrapper implements IGsonWrapper {

    private Gson gson;

    public GsonWrapper(Gson gson) {
        this.gson = gson;
    }

    @Override public <T> T fromJson(String json, Type typeOf) {
        return gson.fromJson(json, typeOf);
    }
}

Thực hiện lớp học của tôi đang được thử nghiệm

class MoviePresenterImp(
        private val gsonWrapper: IGsonWrapper) : MoviePresenter {

    private companion object {
        const val movieKey = "movieKey"
    }

    override fun saveMovieState(movieJson: String) {
            val movieMap = serializeStringToMap(movieJson)

            when (movieMap.getOrElse(movieKey, {""})) {
                /* do something here */
            }
    }

    // Exception return from this method
    private fun serializeStringToMap(ccpaStatus: String): Map<String, String> =
            gsonWrapper.fromJson<Map<String, String>>(ccpaStatus, object : TypeToken<Map<String, String>>() {}.type) // Exception
}

Lớp kiểm tra thực tế, chỉ cần giữ mọi thứ đơn giản

class MoviePresenterImpTest {
    private lateinit var moviePresenterImp: MoviePresenterImp
    private val gsonWrapper: GsonWrapper = GsonWrapper(Gson())
    private val spyGsonWrapper = spy(gsonWrapper)

    @Before
    fun setUp() {
        moviePresenterImp = MoviePresenterImp(spyGsonWrapper)
    }

    @Test
    fun `should not save any movie when there is an error`() {
        // Arrange
        val mapType: Type = object : TypeToken<Map<String, String>>() {}.type
        whenever(spyGsonWrapper.fromJson<Map<String, String>>("{\"movie\":\"movieId\"}", mapType)).thenReturn(null)

        // Act
        moviePresenterImp.saveMovieState("{\"movie\":\"movieId\"}")

        // Assert here
    }
}

Cảm ơn rất nhiều về sựh gợi ý,

Câu trả lời:


3

Nó phụ thuộc vào những gì bạn muốn đạt được. Bạn có muốn cho phép MoviePresenterImp.serializeStringToMaptrở lại null? Hiện tại, điều đó là không thể và đó là những gì bạn đang kiểm tra trong bài kiểm tra đơn vị của mình:

  • Điều gì sẽ xảy ra khi gsonWrapper.fromJsontrở về null?

  • serializeStringToMap sẽ đưa ra một ngoại lệ vì loại trả về của nó được khai báo là không thể rỗng (Kotlin thêm một kiểm tra null dưới mui xe).

Trong thực tế, spyGsonWrapper.fromJsonchỉ trả lại nullnếu gson.fromJsontrả lại null. Theo tài liệu java của Gson, nó chỉ có thể xảy ra nếu jsonđối số là null(nếu jsonkhông hợp lệ, phương thức sẽ ném JsonSyntaxException). Vì vậy, bạn nên:

  • kiểm tra xem jsontham số có nulltrong spyGsonWrapper.fromJsonvà ném IllegalArgumentException nếu có. Điều này sẽ đảm bảo rằng phương thức không bao giờ trả về null(btw. Bạn có thể thêm một @NotNullchú thích, xem chú thích nullable ). Bạn có thể giữ nguyên trạng serializeStringToMapnhưng bạn cần thay đổi bài kiểm tra, vì nó không còn ý nghĩa gì nữa.
  • nếu bạn muốn quay lại nullthay vì ném ngoại lệ, bạn cần thay đổi MoviePresenterImp.serializeStringToMap theo đề xuất của @ duongdt3

Dưới đây là một bài kiểm tra ví dụ:

class MoviePresenterImpTest {

    private lateinit var moviePresenter: MoviePresenterImp
    private lateinit var spyGsonWrapper: GsonWrapper

    @Rule @JvmField
    var thrown = ExpectedException.none();

    @Before
    fun setUp() {
        spyGsonWrapper = Mockito.mock(GsonWrapper::class.java)
        moviePresenter = MoviePresenterImp(spyGsonWrapper)
    }

    @Test
    fun `should not save any movie when GsonWrapper throws an error`() {
        // Given
        Mockito.`when`(spyGsonWrapper.fromJson<Map<String, String>>(anyString(), any(Type::class.java)))
            .thenThrow(JsonSyntaxException("test"))
        // Expect
        thrown.expect(JsonSyntaxException::class.java)
        // When
        moviePresenter.saveMovieState("{\"movie\":\"movieId\"}")
    }

   // Or without mocking at all

    @Test
    fun `should not save any movie when Gson throws error`() {
        // Given
        moviePresenter = MoviePresenterImp(GsonWrapper(Gson()))
        // Expect
        thrown.expect(JsonSyntaxException::class.java)
        // When
        moviePresenter.saveMovieState("Some invalid json")
    }

    // If you want to perform additional checks after an exception was thrown
    // then you need a try-catch block

    @Test
    fun `should not save any movie when Gson throws error and `() {
        // Given
        moviePresenter = MoviePresenterImp(GsonWrapper(Gson()))
        // When
        try {
            moviePresenter.saveMovieState("Some invalid json")
            Assert.fail("Expected JsonSyntaxException")
        } catch(ex : JsonSyntaxException) {}
        // Additional checks
        // ...
    }
}

6

Tôi tìm thấy vấn đề ở đây:

Bạn phải sử dụng bản đồ nullable? thay vì Bản đồ không null trong MoviePresenterImp (mã Kotlin), vì trong lớp Kiểm tra đơn vị, bạn theo dõi gsonWrapper và buộc phương thức 'spyGsonWrapper.fromJson' trả về null.

Bây giờ nó ổn rồi.

fun saveMovieState(movieJson: String) {
        val movieMap = serializeStringToMap(movieJson)

        when (movieMap?.getOrElse(movieKey, { "" })) {
            /* do something here */
        }
    }

    // Exception return from this method
    private fun serializeStringToMap(ccpaStatus: String): Map<String, String>? {
        val type: Type =
            object : TypeToken<Map<String, String>>() {}.type
        return gsonWrapper.fromJson(ccpaStatus, type) // Exception
    }

Xin chào, có mà đã làm việc. Tuy nhiên, tôi không muốn trả lại bản đồ null và tôi muốn giữ nó dưới dạng non-null. Tôi nghĩ rằng những gì tôi đang cố gắng đạt được là trả lại một bản đồ có nội dung không có giá trị. Vì vậy, khi nào movieMap.getOrElse()sẽ trả về một null. Đó là những gì tôi đã cố gắng để chế giễu. Có filmMap chứa null là điều tôi không biết phải làm thế nào?.
ant2009

1
Trên thực tế, về phân tích JSON với Gson, bạn nên hỗ trợ cho trường hợp null, đôi khi văn bản JSON không khớp với mong đợi của chúng tôi. Bạn có một trường hợp thử nghiệm cho null là một ý tưởng tốt.
duongdt3

2

Với thiết lập của bạn, bạn đang tìm kiếm emptyMap()thay vìnull

whenever(spyGsonWrapper.fromJson<Map<String, String>>("{\"movie\":\"movieId\"}", mapType))
    .thenReturn(emptyMap())

Điều này sẽ tuân thủ chữ ký, vì nó không có giá trị

fun serializeStringToMap(ccpaStatus: String): Map<String, String>

Ngoài ra, nó sẽ nhập khối khác trong movieMap.getOrElse()cuộc gọi.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.