AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / coding / 问题

问题[unit-testing](coding)

Martin Hope
wavesinaroom
Asked: 2025-01-08 10:28:31 +0800 CST

断言结构向量长度

  • 4

我使用 rust 的第一个项目是一个类似扑克牌的小型纸牌游戏,该游戏有五列,玩家在其中放置五张牌。是一个从外部APICard获取其值和套件的结构。另一方面,存储我之前谈到的列。 现在,我想通过运行测试来测试某一列不会获得超过五张牌。BoardBoard

卡片:

pub struct Card {
    value: String,
    suit: String,
}

impl Card {
    pub fn new(value: String, suit: String) -> Card {
        Card { value, suit }
    }
}

木板:

use crate::card::Card;

struct Board {
    columns: [Vec<Card>;5]
}

impl Board {
    fn add_card(self){# adds a card}

测试:

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn no_card_full_column() {
        let card_one = Card::new(String::from("4"), String::from("Spades"));
        let card_two = Card::new(String::from("3"), String::from("Spades"));
        let card_three = Card::new(String::from("2"), String::from("Spades"));
        let card_four = Card::new(String::from("5"), String::from("Spades"));
        let card_five = Card::new(String::from("6"), String::from("Spades"));
        let mut board = Board {
            columns: [vec![], vec![], vec![], vec![], vec![]],
        };
        board.columns[0].push(card_one);
        board.columns[0].push(card_two);
        board.columns[0].push(card_three);
        board.columns[0].push(card_four);
        board.columns[0].push(card_five);

        board.add_card();
        assert_eq!(&board.columns[0].len(), 5);
    }
}

rust-analyzer抱怨can't compare &usize with integer(顺便说一下我知道 usize 借用)所以我有几个问题:

  1. usize结构向量是否以不同于的方式分配内存integer?
  2. 我查阅了文档,但没有找到任何有用的信息,所以我尝试将其转换usize为整数,我知道这并不优雅,但我想通过测试并在以后改进它(绿色、红色、重构)。我不认为转换长度是答案。但我还应该继续尝试吗?
unit-testing
  • 1 个回答
  • 25 Views
Martin Hope
Enlico
Asked: 2024-12-28 14:03:08 +0800 CST

在生产代码中发现了伪造的 IO 类型类的实例,但在测试中却没有发现

  • 7

我有很多IO基于的操作,其中最简单的一个如下:

-- Loop.hs
module Loop where
import System.Console.ANSI (setCursorPosition)
type Pos = (Int, Int)
setCursorPosition' :: Pos -> IO ()
setCursorPosition' = uncurry setCursorPosition

此时,从上面的开始,我决定根据实现的类型约束来编写这些函数,而不是像这个答案所建议的那样IO硬编码。IO

所以我做的是

  • 定义一个FakeIO类型class及其简单实现IO:
    -- Interfaces.hs
    module Interfaces where
    import qualified System.Console.ANSI as ANSI (setCursorPosition)
    
    class FakeIO m where
      setCursorPosition :: Int -> Int -> m ()
    
    instance FakeIO IO where
      setCursorPosition = ANSI.setCursorPosition
    
  • 更改setCursorPosition'为使用这个接口:
    -- Loop.hs
    module Loop where
    import Interfaces
    type Pos = (Int, Int)
    setCursorPosition' :: FakeIO m => Pos -> m ()
    setCursorPosition' = uncurry setCursorPosition
    

这使得程序仍然可以正常工作(通过cabal run),证明“重构”是正确的。

但当我尝试利用此重构进行测试时,我遇到了困难。我所做的就是编写以下测试:

-- test/Main.hs
module Main where

import Control.Monad (unless)
import System.Exit (exitFailure)
import MTLPrelude (State, execState, modify')
import Test.QuickCheck

import Loop
import Interfaces

data MockTerminal = MockTerminal {
  pos :: (Int, Int)
} deriving Eq

instance FakeIO (State MockTerminal) where
  setCursorPosition y x = modify' $ \m -> MockTerminal { pos = (y, x) }

main :: IO ()
main = do
  result <- quickCheckResult tCenter
  unless (isSuccess result) exitFailure

tCenter :: Bool
tCenter = (setCursorPosition' (1,1))
          `execState` MockTerminal { pos = (0,0)}
          == MockTerminal { pos = (1,1) }

编译失败(通过cabal test),因为

error: [GHC-39999]
    • No instance for ‘snakegame-0.1.0.0:Loop:Interfaces.FakeIO
                         (StateT MockTerminal Identity)’
        arising from a use of ‘setCursorPosition'’
    • In the first argument of ‘execState’, namely
        ‘(setCursorPosition' (1, 1))’
      In the first argument of ‘(==)’, namely
        ‘(setCursorPosition' (1, 1))
           `execState` MockTerminal {pos = (0, 0)}’
      In the expression:
        (setCursorPosition' (1, 1)) `execState` MockTerminal {pos = (0, 0)}
          == MockTerminal {pos = (1, 1)}
   |
41 | tCenter = (setCursorPosition' (1,1))
   |            ^^^^^^^^^^^^^^^^^^

我不明白,因为instance FakeIO (State MockTerminal)应该正是snakegame-0.1.0.0:Loop:Interfaces.FakeIO (StateT MockTerminal Identity)编译器声称不存在的实例。

此外,如果我将测试改为使用setCursorPosition 1 1而不是setCursorPosition' (1,1),它会编译并通过,表明instance确实在发挥作用。

instance因此,这与 的定义相结合时一定出了问题setCursorPosition'。


我把示例缩减为以下 4 个文件:

$ tree !(dist-newstyle)
cabal.project  [error opening dir]
LICENSE  [error opening dir]
Session.vim  [error opening dir]
snakegame.cabal  [error opening dir]
src
├── Interfaces.hs
├── Loop.hs
└── Main.hs
test
└── Main.hs

2 directories, 8 files

其中:

-- src/Main.hs
module Main where

import Loop

main :: IO ()
main = setCursorPosition' (1,1)
-- src/Loop.hs
module Loop (setCursorPosition') where

import Interfaces
type Pos = (Int, Int)

setCursorPosition' :: FakeIO m => Pos -> m ()
setCursorPosition' = uncurry setCursorPosition
-- test/Main.hs
module Main where

import Control.Monad (unless)
import System.Exit (exitFailure)
import MTLPrelude (State, execState, modify')
import Test.QuickCheck

import Loop
import Interfaces

data MockTerminal = MockTerminal {
  pos :: (Int, Int)
} deriving Eq

instance FakeIO (State MockTerminal) where
  setCursorPosition y x = modify' $ \m -> MockTerminal { pos = (y, x) }
  putChar _ = modify' id

main :: IO ()
main = do
  result <- quickCheckResult tCenter
  unless (isSuccess result) exitFailure

tCenter :: Bool
tCenter = (setCursorPosition' (1,1))
          `execState` MockTerminal { pos = (0,0)}
          == MockTerminal { pos = (1,1)}
cabal-version: 3.0
name: snakegame
version: 0.1.0.0

common common
    default-language: GHC2024
    build-depends: base >= 4.19.1.0
                 , ansi-terminal
                 , mtl-prelude

common warnings
    ghc-options: -Wall

executable snakegame
    import: warnings, common
    main-is: Main.hs
    other-modules: Loop
                 , Interfaces
    hs-source-dirs: src

library Loop
    import: warnings, common
    exposed-modules: Loop
    hs-source-dirs: src

library Interfaces
    import: warnings, common
    exposed-modules: Interfaces
    hs-source-dirs: src

test-suite Test
    import: warnings, common
    type: exitcode-stdio-1.0
    main-is: Main.hs
    build-depends: QuickCheck
                 , Interfaces
                 , Loop
    hs-source-dirs: test
packages: .

with-compiler: ghc-9.10.1
unit-testing
  • 1 个回答
  • 54 Views
Martin Hope
Green grün 绿色 vert зеле
Asked: 2024-12-22 13:52:36 +0800 CST

防锈测试:未使用的进口产品

  • 5

尽管我正在使用模块中的所有导入tests,但 Cargo 仍将其标识为未使用。请考虑以下示例:

pub fn f() {}

mod tests {
    use crate::a::f;

    #[test]
    fn test_f() {
        f();
    }
}

编译此程序cargo build会产生以下警告:

warning: unused import: `crate::a::f`
 --> src/a.rs:4:9
  |
4 |     use crate::a::f;
  |         ^^^^^^^^^^^

删除导入自然会导致错误。

Rust 的示例用于import super::*其单元测试,但在我的示例中,我收到了相同的警告:

warning: unused import: `super::*`
 --> src/a.rs:4:9
  |
4 |     use super::*;
  |         ^^^^^^^^

这里有什么问题?

我的 Cargo.toml 是:

[package]
name = "problem"
version = "0.0.1"
edition = "2021"
unit-testing
  • 2 个回答
  • 38 Views
Martin Hope
Abner Matheus
Asked: 2024-11-29 09:28:22 +0800 CST

在 golang 中使用 testfy 进行模拟时出现奇怪的错误

  • 5

我有以下方法:

func (u *UserService) Create(createUserDto *CreateUserDto) (*User, error) {
    userExists, err := u.userRepository.FetchByEmail(createUserDto.Email)
    if err != nil {
        return nil, err
    }

    if userExists != nil {
        return nil, &HTTPError{
            Code:    409,
            Message: "Email already exists",
        }
    }

    passwordHash, err := bcrypt.GenerateFromPassword([]byte(createUserDto.Password), bcrypt.DefaultCost)
    if err != nil {
        return nil, err
    }

    activationCode := uuid.New().String()
    activationCodeExpires := time.Now().Add(30 * time.Minute)

    user := &User{
        Name:                  createUserDto.Name,
        Email:                 createUserDto.Email,
        Password:              string(passwordHash),
        Phone:                 createUserDto.Phone,
        Photo:                 createUserDto.Photo,
        IsActive:              false,
        ActivationCode:        &activationCode,
        ActivationCodeExpires: &activationCodeExpires,
    }

    return u.userRepository.Save(user)
}

我有以下测试:

t.Run("Should return a new user", func(t *testing.T) {
    createUserDtoMock := &CreateUserDto{
        Name:     "John Doe",
        Email:    "[email protected]",
        Password: "password",
        Phone:    nil,
        Photo:    nil,
    }

    var idMock *int64 = new(int64)
    *idMock = 1
    userResponseMock := &User{
        Id:       idMock,
        Name:     "John Doe",
        Email:    "[email protected]",
        Password: "$2a$10$MmWsAi5AJHnWaUG5vvcl7OUpar9kIhzOMbwp1WBB0dZoFeYYebfKC",
        Phone:    nil,
        Photo:    nil,
    }

    userInputMock := &User{
        Id:       nil,
        Name:     "John Doe",
        Email:    "[email protected]",
        Password: "password",
        Phone:    nil,
        Photo:    nil,
    }

    repository := &UserRepositoryMock{}
    service := NewUserService(repository)

    repository.On("FetchByEmail", createUserDtoMock.Email).Return((*User)(nil), nil)
    repository.On("Save", userInputMock).Return(userResponseMock, nil)

    user, err := service.Create(createUserDtoMock)

    assert.NoError(t, err)
    assert.IsType(t, &User{}, user)
    assert.Equal(t, *userResponseMock, *user)

    repository.AssertExpectations(t)
})

然而,当我运行它时,我收到了一个我根本无法理解的错误:

Running tool: /snap/go/current/bin/go test -timeout 30s -run ^TestUserService$/^Should_return_a_new_user$ github.com/theabner/pallet/internal/core/service

--- FAIL: TestUserService (0.09s)
    --- FAIL: TestUserService/Should_return_a_new_user (0.09s)
panic: 
    
    mock: Unexpected Method Call
    -----------------------------
    
    Save(*entity.User)
            0: &entity.User{Id:(*int64)(nil), Name:"John Doe", Email:"[email protected]", Password:"$2a$10$3Z7PAQ0F01Xwb/wAzEsQVuEC1O8adELBCUqeMPnxCYEbZ1mSJDk4S", Phone:(*string)(nil), Photo:(*string)(nil), IsActive:false, ActivationCode:(*string)(0xc000114b40), ActivationCodeExpires:time.Date(2024, time.November, 28, 22, 55, 20, 331423224, time.Local), ResetPasswordToken:(*string)(nil), ResetPasswordTokenExpires:<nil>, CreatedAt:<nil>, UpdatedAt:<nil>}
    
    The closest call I have is: 
    
    Save(*entity.User)
            0: &entity.User{Id:(*int64)(nil), Name:"John Doe", Email:"[email protected]", Password:"password", Phone:(*string)(nil), Photo:(*string)(nil), IsActive:false, ActivationCode:(*string)(nil), ActivationCodeExpires:<nil>, ResetPasswordToken:(*string)(nil), ResetPasswordTokenExpires:<nil>, CreatedAt:<nil>, UpdatedAt:<nil>}
    
    Difference found in argument 0:
    
    --- Expected
    +++ Actual
    @@ -4,3 +4,3 @@
      Email: (string) (len=17) "[email protected]",
    - Password: (string) (len=8) "password",
    + Password: (string) (len=60) "$2a$10$3Z7PAQ0F01Xwb/wAzEsQVuEC1O8adELBCUqeMPnxCYEbZ1mSJDk4S",
      Phone: (*string)(<nil>),
    @@ -8,4 +8,4 @@
      IsActive: (bool) false,
    - ActivationCode: (*string)(<nil>),
    - ActivationCodeExpires: (*time.Time)(<nil>),
    + ActivationCode: (*string)((len=36) "d33db295-c9c8-411b-a6fc-3818510adc59"),
    + ActivationCodeExpires: (*time.Time)(2024-11-28 22:55:20.331423224 -0300 -03 m=+1800.089611904),
      ResetPasswordToken: (*string)(<nil>),
    
    Diff: 0: FAIL:  (*entity.User=&{<nil> John Doe [email protected] $2a$10$3Z7PAQ0F01Xwb/wAzEsQVuEC1O8adELBCUqeMPnxCYEbZ1mSJDk4S <nil> <nil> false 0xc000114b40 2024-11-28 22:55:20.331423224 -0300 -03 m=+1800.089611904 <nil> <nil> <nil> <nil>}) != (*entity.User=&{<nil> John Doe [email protected] password <nil> <nil> false <nil> <nil> <nil> <nil> <nil> <nil>})
    at: [/home/theabnermatheus/Workspace/personal_projects/pallet/internal/shared/mocks/repository/user_repository_mock.go:13 /home/theabnermatheus/Workspace/personal_projects/pallet/internal/core/service/user_service.go:55 /home/theabnermatheus/Workspace/personal_projects/pallet/internal/core/service/user_service_test.go:105]
     [recovered]
    panic: 
    
    mock: Unexpected Method Call
    -----------------------------
    
    Save(*entity.User)
            0: &entity.User{Id:(*int64)(nil), Name:"John Doe", Email:"[email protected]", Password:"$2a$10$3Z7PAQ0F01Xwb/wAzEsQVuEC1O8adELBCUqeMPnxCYEbZ1mSJDk4S", Phone:(*string)(nil), Photo:(*string)(nil), IsActive:false, ActivationCode:(*string)(0xc000114b40), ActivationCodeExpires:time.Date(2024, time.November, 28, 22, 55, 20, 331423224, time.Local), ResetPasswordToken:(*string)(nil), ResetPasswordTokenExpires:<nil>, CreatedAt:<nil>, UpdatedAt:<nil>}
    
    The closest call I have is: 
    
    Save(*entity.User)
            0: &entity.User{Id:(*int64)(nil), Name:"John Doe", Email:"[email protected]", Password:"password", Phone:(*string)(nil), Photo:(*string)(nil), IsActive:false, ActivationCode:(*string)(nil), ActivationCodeExpires:<nil>, ResetPasswordToken:(*string)(nil), ResetPasswordTokenExpires:<nil>, CreatedAt:<nil>, UpdatedAt:<nil>}
    
    Difference found in argument 0:
    
    --- Expected
    +++ Actual
    @@ -4,3 +4,3 @@
      Email: (string) (len=17) "[email protected]",
    - Password: (string) (len=8) "password",
    + Password: (string) (len=60) "$2a$10$3Z7PAQ0F01Xwb/wAzEsQVuEC1O8adELBCUqeMPnxCYEbZ1mSJDk4S",
      Phone: (*string)(<nil>),
    @@ -8,4 +8,4 @@
      IsActive: (bool) false,
    - ActivationCode: (*string)(<nil>),
    - ActivationCodeExpires: (*time.Time)(<nil>),
    + ActivationCode: (*string)((len=36) "d33db295-c9c8-411b-a6fc-3818510adc59"),
    + ActivationCodeExpires: (*time.Time)(2024-11-28 22:55:20.331423224 -0300 -03 m=+1800.089611904),
      ResetPasswordToken: (*string)(<nil>),
    
    Diff: 0: FAIL:  (*entity.User=&{<nil> John Doe [email protected] $2a$10$3Z7PAQ0F01Xwb/wAzEsQVuEC1O8adELBCUqeMPnxCYEbZ1mSJDk4S <nil> <nil> false 0xc000114b40 2024-11-28 22:55:20.331423224 -0300 -03 m=+1800.089611904 <nil> <nil> <nil> <nil>}) != (*entity.User=&{<nil> John Doe [email protected] password <nil> <nil> false <nil> <nil> <nil> <nil> <nil> <nil>})
    at: [/home/theabnermatheus/Workspace/personal_projects/pallet/internal/shared/mocks/repository/user_repository_mock.go:13 /home/theabnermatheus/Workspace/personal_projects/pallet/internal/core/service/user_service.go:55 /home/theabnermatheus/Workspace/personal_projects/pallet/internal/core/service/user_service_test.go:105]
    

goroutine 23 [running]:
testing.tRunner.func1.2({0x616fe0, 0xc000115060})
    /snap/go/10743/src/testing/testing.go:1632 +0x230
testing.tRunner.func1()
    /snap/go/10743/src/testing/testing.go:1635 +0x35e
panic({0x616fe0?, 0xc000115060?})
    /snap/go/10743/src/runtime/panic.go:785 +0x132
github.com/stretchr/testify/mock.(*Mock).fail(0xc0001389b0, {0x675778?, 0x4?}, {0xc000138a00?, 0x1?, 0x1?})
    /home/theabnermatheus/go/pkg/mod/github.com/stretchr/[email protected]/mock/mock.go:349 +0x12d
github.com/stretchr/testify/mock.(*Mock).MethodCalled(0xc0001389b0, {0x714c61, 0x4}, {0xc000114b50, 0x1, 0x1})
    /home/theabnermatheus/go/pkg/mod/github.com/stretchr/[email protected]/mock/mock.go:509 +0x5d7
github.com/stretchr/testify/mock.(*Mock).Called(0xc0001389b0, {0xc000114b50, 0x1, 0x1})
    /home/theabnermatheus/go/pkg/mod/github.com/stretchr/[email protected]/mock/mock.go:481 +0x125
github.com/theabner/pallet/internal/shared/mocks/repository.(*UserRepositoryMock).Save(0xc0001389b0, 0xc00017cd80)
    /home/theabnermatheus/Workspace/personal_projects/pallet/internal/shared/mocks/repository/user_repository_mock.go:13 +0x7d
github.com/theabner/pallet/internal/core/service.(*UserService).Create(0xc0001a3f10, 0xc0001a3f20)
    /home/theabnermatheus/Workspace/personal_projects/pallet/internal/core/service/user_service.go:55 +0x2e2
github.com/theabner/pallet/internal/core/service.TestUserService.func5(0xc000174d00)
    /home/theabnermatheus/Workspace/personal_projects/pallet/internal/core/service/user_service_test.go:105 +0x325
testing.tRunner(0xc000174d00, 0x693bc0)
    /snap/go/10743/src/testing/testing.go:1690 +0xf4
created by testing.(*T).Run in goroutine 22
    /snap/go/10743/src/testing/testing.go:1743 +0x390
FAIL    github.com/theabner/pallet/internal/core/service    0.099s
FAIL

帮帮我,我已经尽力了。

这段代码需要做的就是检查是否已经有使用注册邮箱的用户,如果没有,就对密码进行哈希处理,创建一个带有到期时间的激活哈希,然后将其发送到作为接口的存储库。

unit-testing
  • 1 个回答
  • 37 Views
Martin Hope
Skru
Asked: 2024-09-23 21:30:47 +0800 CST

如何在 Rust 测试期间模拟其他模块的私有结构

  • 5

我有一个包含多个域的应用程序,这些域具有不同的职责。我在下面添加了一个简化的工作示例。

该User结构由域拥有auth。其字段和new()构造函数是该域私有的。其他域只能使用域服务作为接口来获取用户。

域函数blog需要User作为输入参数(get_posts_by_user()在示例中),或者它们需要调用域函数auth(get_posts_by_user_id()在示例中)。为了测试这些函数,我需要创建实例,User而不使用需要数据库的实际AuthService实现。相反,我想模拟并让模拟返回我需要测试AuthService的任何边缘情况。User

User 和 Blog 只是示例。实际上,我可能会遇到数十个这样的域私有类型的案例。我正在为所有这些案例寻找类似的解决方案。

我想到了几种解决这个问题的方法,但没有一种能让我信服:

  • 使所有字段User公开,或使构造函数new()公开。
    • 缺点:所有其他域都能够创建经过身份验证的用户,这些用户可能会(意外)被滥用,并可能在以后产生安全问题
  • 在身份验证域中创建一个公共模拟,为用户实现自己的构造函数
    • User缺点:这意味着构造函数的代码部分重复。维护成本更高,因为每次更改时模拟构造函数也需要更新
  • 复制结构以#[cfg(test)]使字段仅在测试期间公开
    • 缺点:代码重复,普通模式和测试模式有不同的行为
    • 这是我在下面的片段中使用的替代方案。
    • 也许可以通过宏自动化
  • 使用一些库
    • 缺点:我还没有找到适合这种情况的有用的库

是否有一些最佳实践来模拟和测试这种情况?

Rust 游乐场

// Authentication domain
pub mod auth {
    // The User struct is constructed by this domain when a user is successfully
    // authenticated
    // private fields, used in production
    #[cfg(not(test))]
    #[derive(Clone, Debug)]
    pub struct User {
        user_id: i32,
        username: String,
    }

    // all fields public, used only in tests
    #[cfg(test)]
    #[derive(Clone, Debug)]
    pub struct User {
        pub user_id: i32,
        pub username: String,
    }

    impl User {
        // new() is private because no other domain should be able to construct a User.
        fn new(user_id: i32, username: String) -> User {
            User { user_id, username }
        }

        // Public read-only access to user id
        pub fn user_id(&self) -> i32 {
            self.user_id
        }

        // Public read-only access to username
        pub fn username(&self) -> &str {
            &self.username
        }
    }

    // Abstraction via trait to decouple services and to allow creation of mocks
    // for easier unit testing
    pub trait AuthService {
        fn get_user(&self, user_id: i32) -> User;
    }

    #[derive(Clone, Debug)]
    pub struct AuthImpl;

    // Implementation of AuthService
    impl AuthService for AuthImpl {
        fn get_user(&self, user_id: i32) -> User {
            // DB access and complex calculations to authenticae user
            std::thread::sleep(std::time::Duration::from_millis(2000));
            User::new(
                user_id,
                vec!["Alice", "Bob", "Claire", "Daniel"]
                    .get(user_id as usize % 4)
                    .unwrap()
                    .to_string(),
            )
        }
    }

    #[cfg(test)]
    mod tests {
        // omitted
    }
}

// Blog domain
pub mod blog {
    use crate::auth::{AuthService, User};

    // Trait for blog service that needs access to the user or to the auth domain
    pub trait BlogService {
        fn get_posts_by_user(&self, user: &User) -> Vec<String>;
        fn get_posts_by_user_id(&self, auth: impl AuthService, user_id: i32) -> Vec<String>;
    }

    #[derive(Clone, Debug)]
    pub struct BlogImpl;

    impl BlogService for BlogImpl {
        // The User from the auth domain is needed as input here.
        // During testing this should be moked
        fn get_posts_by_user(&self, user: &User) -> Vec<String> {
            // this would be some call to the data storage
            vec![format!(
                "Hi, my name is {} (id: {})",
                user.username(),
                user.user_id()
            )]
        }

        // A call to AuthService is needed here to authenticate the user.
        // During testing this should be mocked
        fn get_posts_by_user_id(&self, auth: impl AuthService, user_id: i32) -> Vec<String> {
            let user = auth.get_user(user_id);
            // this would be some call to the data storage
            vec![format!(
                "Hi, my name is {} (id: {})",
                user.username(),
                user.user_id()
            )]
        }
    }

    // Unittest the service
    #[cfg(test)]
    mod tests {
        use crate::auth::{AuthService, User};

        use super::*;

        // Mock of AuthService to test BlogService
        #[derive(Clone, Debug)]
        struct AuthMock {
            // Mocked value for User. But user is private in auth domain. How to mock it here?
            pub user: User,
        }

        impl AuthService for AuthMock {
            fn get_user(&self, _user_id: i32) -> User {
                self.user.clone()
            }
        }

        #[test]
        fn test_get_posts_by_user() {
            let blog = BlogImpl;
            // User is private in auth domain. What is the best way to mock it here?
            let user = User {
                user_id: 42,
                username: "Dummy".to_string(),
            };
            let posts = blog.get_posts_by_user(&user);
            assert_eq!(posts, vec!["Hi, my name is Dummy (id: 42)"]);
        }

        #[test]
        fn test_get_posts_by_user_id() {
            let blog = BlogImpl;
            let auth_mock = AuthMock {
                // User is private in auth domain. What is the best way to mock it here?
                user: User {
                    user_id: 1337,
                    username: "Admin".to_string(),
                },
            };
            let posts = blog.get_posts_by_user_id(auth_mock, 1);
            assert_eq!(posts, vec!["Hi, my name is Admin (id: 1337)"]);
        }
    }
}

use crate::auth::{AuthImpl, AuthService};
use crate::blog::{BlogImpl, BlogService};

fn main() {
    let auth = AuthImpl;
    let blog = BlogImpl;

    let now = std::time::SystemTime::now();
    println!("{} ms", now.elapsed().unwrap().as_millis());

    // get user form auth domain
    println!("User: {:?}", auth.get_user(123));
    println!("{} ms", now.elapsed().unwrap().as_millis());

    println!("Posts by user: {:?}", blog.get_posts_by_user(&auth.get_user(1234)));
    println!("{} ms", now.elapsed().unwrap().as_millis());

    println!("Posts by user id: {:?}", blog.get_posts_by_user_id(auth, 12345));
    println!("{} ms", now.elapsed().unwrap().as_millis());
}
unit-testing
  • 1 个回答
  • 34 Views
Martin Hope
MiguelG
Asked: 2024-09-05 14:00:42 +0800 CST

如何使用 redux-toolkit vitest 监视 useAppDispatch() 钩子实例

  • 7

由于我们不能在测试文件中使用钩子 useAppDispatch(),我想知道如何监视返回的实例。

import * as redux from "@/core/context/redux/store";

...

it("test",async ()=>{
    await waitFor(() => {
      wrapper = render(<MockForgeViewer />);
    });
    const spyDispatch = vi.spyOn(redux, "useAppDispatch");
    wrapper?.unmount();
    expect(spyDispatch).toHaveBeenCalledWith();
   
});

我刚刚意识到,此代码正在监视钩子本身,而不是返回值(dispatch)

const dispatch = useAppDispatch();

实际上我希望监视调度,以断言何时调用了特定的reducer。例如:

dispatch(resetStates());
unit-testing
  • 1 个回答
  • 19 Views
Martin Hope
kirin
Asked: 2024-07-01 04:33:29 +0800 CST

如何对 multipart.Form 进行单元测试

  • 6

我需要为该功能编写测试:

func Parse(f *multipart.Form) ([]Person, error)

需要说明的是,multipart.Form.Value不仅仅是key:value,那是更复杂的json,简单的例子:

[
    {
        "value": {
            "person_name__0": "Mike",
            "person_name__1": "John"
        },
        "question": {
            "id": 52681363,
            "slug": "person_name"
        }
    },
    {
        "value": {
            "created_date__0": "2024-06-26",
            "created_date__1": "2024-06-24"
        },
        "question": {
            "id": 52681362,
            "slug": "created_date"
        }
    },
    {
        "value": "Germany",
        "question": {
            "id": 52681360,
            "slug": "country"
        }
    }
]

测试该函数的最佳方法是什么?我应该手动创建 json 文件(或代码中的 json 字符串),从中创建 multipart.Form 对象,还是应该自动创建此类文件(我认为这很难)?或者我可以以某种方式创建模拟对象?但是我如何创建具有相同 .Value 结构的模拟 multipart.Form 对象(我的意思是相同的 json 结构,就像我的例子一样)?

这是我第一次尝试测试某些东西,可能会有一些愚蠢的问题,但我希望你能告诉我哪里错了并帮助我=)

unit-testing
  • 1 个回答
  • 51 Views
Martin Hope
Ala Eddine Menai
Asked: 2024-06-05 21:23:13 +0800 CST

为什么测试日期在 localhost 中通过但在 Azure 管道中失败?

  • 5

我有这个单元测试:

import { describe, expect, it } from "@jest/globals"

import { format2APIDate } from "../helpers"

describe("format2APIDate()", () => {
  it("should return a date with the format: YYYY/MM/DD for date with this format YYYY-MM-DD", () => {
    const date = new Date("2024-02-12")

    const formattedDate = format2APIDate(date)

    expect(formattedDate).toBe("2024/02/12")
  })

  it("should return a date with the format: YYYY/MM/DD when a date has this format", () => {
    const date = new Date("Mon Feb 12 2024 00:00:00 GMT+0100 (Central European Standard Time)")

    const formattedDate = format2APIDate(date)

    expect(formattedDate).toBe("2024/02/12")
  })
})

当我在本地运行测试时:

npm run test。

他们通过了。

在此输入图像描述

但是,当我合并更改时,最后一个测试失败:

在此输入图像描述

这是 format2APIDate:

export const format2APIDate = (date: Date) => {
  // Format the date as "YYYY/MM/DD"
  const formattedDate = new Date(date)
  return `${formattedDate.getFullYear()}/${String(formattedDate.getMonth() + 1).padStart(2, "0")}/${String(formattedDate.getDate()).padStart(2, "0")}`
}

unit-testing
  • 2 个回答
  • 28 Views
Martin Hope
Joe Casadonte
Asked: 2024-02-24 10:25:27 +0800 CST

Test2::Mock 发出“原型不匹配”警告

  • 6

运行以下代码:

#!/usr/bin/env perl

use 5.038;

use Test2::Mock;
use Time::HiRes;

my($mock) = Test2::Mock->new(
    class => 'Time::HiRes',
    override => [
        gettimeofday => sub { return 1; },
    ]
);

my($foo) = Time::HiRes::gettimeofday;
say $foo;

结果是这样的输出:

Prototype mismatch: sub Time::HiRes::gettimeofday () vs none at /opt/perl/lib/site_perl/5.38.0/Test2/Mock.pm line 434.
1
Prototype mismatch: sub Time::HiRes::gettimeofday: none vs () at /opt/perl/lib/site_perl/5.38.0/Test2/Mock.pm line 452.

我不知道如何摆脱原型不匹配警告。我尝试了很多没有效果的方法(其中一些是出于绝望,而不是因为我认为它们会起作用):

  • 添加原型到 anon sub
gettimeofday => sub () { return 1; },
  • 在块中定义了一个带有原型的命名子BEGIN并使用它
BEGIN {
    sub gtod () { return 1; }
}

my($mock) = Test2::Mock->new(
    class => 'Time::HiRes',
    override => [
        gettimeofday => \&gtod,
    ]
);
  • Time::Hires之前使用Test2::Mock
  • 结合上述所有内容导入并覆盖它,gettimeofday例如:main
use Test2::Mock;
use Time::HiRes qw(gettimeofday);

my($mock) = Test2::Mock->new(
    class => 'main',
    override => [
        gettimeofday => sub () { return 1; },
    ]
);

my($foo) = gettimeofday;
say $foo;
  • 将其包裹在一个no warnings 'prototype'块中
{
    no warnings 'prototype';

    my($mock) = Test2::Mock->new(
        class => 'main',
        override => [
            gettimeofday => sub () { return 1; },
        ]
    );
}

没有什么区别。

我的代码可以工作,但有一个警告是有原因的,我真的很想以正确的方式处理它。我通过谷歌搜索找到的最好答案是通过修改来抑制消息$SIG{__WARN__},如果还有其他选择的话,这似乎是一个坏主意。

注意:我知道Test::Mock::Time,,Test::MockTime和Test::MockTime::HiRes- 他们没有做我需要他们做的事情,所以我想我应该自己做。另外,我已经看过Sub::Prototype但还没有尝试过,因为它不是核心模块,我宁愿不去那里,除非没有其他选择(此时我可以修改$SIG{__WARN__}。

unit-testing
  • 1 个回答
  • 35 Views
Martin Hope
RoyM
Asked: 2024-02-04 07:23:15 +0800 CST

jest.spyOn 似乎没有正确模拟默认导出函数

  • 5

我似乎无法弄清楚为什么这个测试失败了;我已将问题减少到这两个文件..

[文件:esm-user.js]

const getJack = () => {return 'Jack';};
const getJill = () => {return 'Jill';};
const getUsers = () => {return [getJack(), getJill()];};
export default { getUsers, getJack };

[文件:esm-user-spy.spec.js]

import users from './esm-user';

describe("Mocks and Spies", () => {
  test.only("partial mock (jack)", () => {
    var getJackSpy = jest.spyOn(users, 'getJack').mockReturnValue('Mock Jack');
    expect(users.getUsers()).toEqual(['Mock Jack', 'Jill']);
  });
});

[输出]

expect(received).toEqual(expected) // deep equality
  Array [
-   "Mock Jack",
+   "Jack",
    "Jill",
  ]
unit-testing
  • 1 个回答
  • 23 Views

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    重新格式化数字,在固定位置插入分隔符

    • 6 个回答
  • Marko Smith

    为什么 C++20 概念会导致循环约束错误,而老式的 SFINAE 不会?

    • 2 个回答
  • Marko Smith

    VScode 自动卸载扩展的问题(Material 主题)

    • 2 个回答
  • Marko Smith

    Vue 3:创建时出错“预期标识符但发现‘导入’”[重复]

    • 1 个回答
  • Marko Smith

    具有指定基础类型但没有枚举器的“枚举类”的用途是什么?

    • 1 个回答
  • Marko Smith

    如何修复未手动导入的模块的 MODULE_NOT_FOUND 错误?

    • 6 个回答
  • Marko Smith

    `(表达式,左值) = 右值` 在 C 或 C++ 中是有效的赋值吗?为什么有些编译器会接受/拒绝它?

    • 3 个回答
  • Marko Smith

    在 C++ 中,一个不执行任何操作的空程序需要 204KB 的堆,但在 C 中则不需要

    • 1 个回答
  • Marko Smith

    PowerBI 目前与 BigQuery 不兼容:Simba 驱动程序与 Windows 更新有关

    • 2 个回答
  • Marko Smith

    AdMob:MobileAds.initialize() - 对于某些设备,“java.lang.Integer 无法转换为 java.lang.String”

    • 1 个回答
  • Martin Hope
    Fantastic Mr Fox msvc std::vector 实现中仅不接受可复制类型 2025-04-23 06:40:49 +0800 CST
  • Martin Hope
    Howard Hinnant 使用 chrono 查找下一个工作日 2025-04-21 08:30:25 +0800 CST
  • Martin Hope
    Fedor 构造函数的成员初始化程序可以包含另一个成员的初始化吗? 2025-04-15 01:01:44 +0800 CST
  • Martin Hope
    Petr Filipský 为什么 C++20 概念会导致循环约束错误,而老式的 SFINAE 不会? 2025-03-23 21:39:40 +0800 CST
  • Martin Hope
    Catskul C++20 是否进行了更改,允许从已知绑定数组“type(&)[N]”转换为未知绑定数组“type(&)[]”? 2025-03-04 06:57:53 +0800 CST
  • Martin Hope
    Stefan Pochmann 为什么 {2,3,10} 和 {x,3,10} (x=2) 的顺序不同? 2025-01-13 23:24:07 +0800 CST
  • Martin Hope
    Chad Feller 在 5.2 版中,bash 条件语句中的 [[ .. ]] 中的分号现在是可选的吗? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench 为什么双破折号 (--) 会导致此 MariaDB 子句评估为 true? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng 为什么 `dict(id=1, **{'id': 2})` 有时会引发 `KeyError: 'id'` 而不是 TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob:MobileAds.initialize() - 对于某些设备,“java.lang.Integer 无法转换为 java.lang.String” 2024-03-20 03:12:31 +0800 CST

热门标签

python javascript c++ c# java typescript sql reactjs html

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve