You should be able to use factor and level with mutate. Can you please provide a reproducible example illustrating the problems you're having with your code?
But if your levels are unordered, you do not need to specify the levels. Using just factor and as.factor is enough. Compare z3 and z4, and check their levels and that of z2.
set.seed(seed = 47715)
library(dplyr)
#>
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#>
#> filter, lag
#> The following objects are masked from 'package:base':
#>
#> intersect, setdiff, setequal, union
fake_df <- tibble(x = seq.int(to = 30),
y = runif(n = 30,
min = -100,
max = 100))
fake_df_mod <- fake_df %>%
mutate(z1 = case_when(y < -50 ~ "very_low",
y < 0 ~ "low",
y < 50 ~ "high",
TRUE ~ "very high"),
z2 = factor(x = z1,
levels = c("very low", "low", "high", "very high"),
ordered = TRUE),
z3 = factor(x = z1),
z4 = as.factor(x = z1))
str(object = fake_df_mod)
#> Classes 'tbl_df', 'tbl' and 'data.frame': 30 obs. of 6 variables:
#> $ x : int 1 2 3 4 5 6 7 8 9 10 ...
#> $ y : num 86.806 13.028 -0.782 -17.397 99.254 ...
#> $ z1: chr "very high" "high" "low" "low" ...
#> $ z2: Ord.factor w/ 4 levels "very low"<"low"<..: 4 3 2 2 4 NA NA 4 NA 3 ...
#> $ z3: Factor w/ 4 levels "high","low","very high",..: 3 1 2 2 3 4 4 3 4 1 ...
#> $ z4: Factor w/ 4 levels "high","low","very high",..: 3 1 2 2 3 4 4 3 4 1 ...
Created on 2019-12-20 by the reprex package (v0.3.0)
Another point is that based on your code, cut seems a better option than case_when in this specific scenario.